Android視圖狀態以及重繪

一、視圖狀態(View States)

1. 五種核心狀態
狀態作用修改方法特點
enabled視圖是否響應交互setEnabled(boolean)禁用狀態下不響應onTouch事件
focused視圖是否獲得焦點requestFocus()需同時滿足focusable和focusableInTouchMode
window_focused視圖所在窗口是否在前臺系統自動維護應用無法直接修改
selected視圖是否被選中setSelected(boolean)同一界面允許多個視圖同時選中
pressed視圖是否被按下setPressed(boolean)通常由系統自動設置點擊狀態
2. 狀態變更響應流程

關鍵源碼解析

  1. 狀態變更入口

    // View.java
    protected void drawableStateChanged() {Drawable d = mBackground;if (d != null && d.isStateful()) {d.setState(getDrawableState()); // 傳遞新狀態給Drawable}
    }
  2. 狀態匹配原理

    // StateListDrawable.java
    protected boolean onStateChange(int[] stateSet) {int idx = findStateIndex(stateSet); // 匹配selector中對應的itemreturn selectDrawable(idx); // 切換Drawable
    }
  3. 觸發重繪

    // StateListDrawable.java
    public boolean selectDrawable(int idx) {// ...更新DrawableinvalidateSelf(); // 關鍵重繪觸發
    }

二、重繪機制(View Invalidation)

1. 兩種重繪方式對比
方法觸發流程應用場景性能影響
invalidate()僅重走draw()流程內容變化但尺寸不變(如文字/顏色)低開銷,局部刷新
requestLayout()完整measure-layout-draw視圖尺寸/結構變化(如添加子View)高開銷,全局重新布局
2. invalidate() 核心流程

源碼關鍵路徑

// ViewRootImpl.java
void scheduleTraversals() {sendEmptyMessage(DO_TRAVERSAL); // 發送異步消息
}public void handleMessage(Message msg) {if (msg.what == DO_TRAVERSAL) {performTraversals(); // 最終入口}
}private void performTraversals() {// 根據標記位決定流程if (!mLayoutRequested) {// 僅執行draw流程performDraw();}
}
3. 性能優化要點
  1. 減少重繪范圍

    // 只刷新局部區域
    public void invalidate(Rect dirty) {// 計算臟區域并傳遞
    }
  2. 避免過度重繪

    • 使用View.setWillNotDraw(true)跳過無內容視圖

    • 合并狀態變更(避免連續多次invalidate)


三、常見問題

Q1:按下按鈕時背景圖切換的完整流程?

A

  1. 狀態變更View.setPressed(true)更新狀態數組

  2. 通知DrawabledrawableStateChanged()調用StateListDrawable.setState()

  3. 匹配資源StateListDrawable.onStateChange()查找對應狀態圖片

  4. 觸發重繪selectDrawable()?→?invalidateSelf()?→?View.invalidate()

  5. 繪制執行:遞歸至ViewRootImpl.scheduleTraversals()?→ 下一幀觸發draw()流程

Q2:invalidate() 和 requestLayout() 的本質區別?

A

  • invalidate()

    • 僅設置DIRTY標記 → 觸發draw()流程

    • 不重新測量/布局 → 適用于內容變化但尺寸不變場景

  • requestLayout()

    • 設置FORCE_LAYOUT標記 → 觸發完整measure-layout-draw

    • 向父視圖遞歸 → 可能引發全局重新布局

Q3:為什么StateListDrawable能自動切換圖片?

A:核心機制是狀態匹配+重繪觸發

  1. res/drawable中定義<selector>狀態映射

  2. onStateChange()用狀態數組匹配最佳item下標

  3. selectDrawable()切換當前Drawable并調用invalidateSelf()

Q4:自定義View如何優化重繪性能?

A:三級優化策略:

  1. 減少區域

    // 只刷新變化區域
    invalidate(dirtyRect);
  2. 避免過度繪制

    • 覆寫hasOverlappingRendering()返回false

    • 使用canvas.clipRect()限制繪制區域

  3. 復用資源

    • 預初始化Paint/Path等對象

    • 使用View.setLayerType(LAYER_TYPE_HARDWARE)啟用硬件加速

Q5:解釋下?scheduleTraversals()?中發送異步消息的意義?是否在主線程執行?

A
核心是通過異步消息+同步屏障確保UI更新的及時性

  1. 異步消息DO_TRAVERSAL?消息被標記為異步類型,優先于普通消息處理

  2. 同步屏障

    • 在消息隊列插入屏障,阻塞后續同步消息

    • 僅允許異步的UI更新消息通過

  3. 主線程執行

    • 消息最終由?ViewRootImpl?的?Handler?在主線程處理

    • 調用?performTraversals()?執行完整的視圖樹遍歷

  4. 設計目的

    • 解決UI更新被業務消息阻塞的問題

    • 保證16ms內完成繪制(60Hz刷新率)

使用代碼證明主線程執行
在?performTraversals()?中可檢查線程:

void performTraversals() {if (Thread.currentThread() != mThread) {throw new RuntimeException("Must be on UI thread!");}// ...measure/layout/draw...
}

其中?mThread?即?ViewRootImpl?創建時的主線程。

Q6:View.postInvalidate() 和 invalidate() 區別?

A

維度invalidate()postInvalidate()
調用線程僅UI線程任意線程
內部實現直接操作視圖樹通過Handler轉發到UI線程
適用場景視圖內部狀態變更后臺線程觸發的UI更新

四、總結

Q:請解釋Android視圖狀態變更如何觸發界面更新?

A
整個過程分為四個關鍵階段:

  1. 狀態變更

    • 調用setPressed()/setSelected()等方法改變視圖狀態

    • 更新視圖內部的mDrawableState狀態數組

  2. Drawable響應

    • 觸發drawableStateChanged()回調

    • StateListDrawable通過onStateChange()匹配新狀態對應的Drawable資源

  3. 重繪調度

    • 調用invalidateSelf()?→ 觸發View.invalidate()

    • 通過ViewParent鏈遞歸至ViewRootImpl

    • 通過scheduleTraversals()異步調度重繪

  4. 繪制執行

    • 下一幀觸發performTraversals()

    • 根據標記位僅執行draw流程(measure/layout跳過)

    • 調用View.draw()?→?Drawable.draw()渲染新狀態對應的圖片

性能優化要點

  • 優先使用invalidate(Rect)局部刷新

  • 復雜動畫啟用硬件加速(LAYER_TYPE_HARDWARE

  • 避免在draw()中創建對象

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/92190.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/92190.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/92190.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue3接收SSE流數據進行實時渲染日志

后端使用的是 Spring Boot WebFlux&#xff08;響應式編程框架&#xff09;&#xff0c;并且返回的是 Server-Sent Events (SSE) 流式數據&#xff0c;那么在 Vue3 中&#xff0c;需要使用 EventSource API 或 fetch 流式讀取 來正確獲取響應內容。方案 1&#xff1a;使用 Eve…

每日五個pyecharts可視化圖表-bars(6)

探索pyecharts庫中條形圖的高級用法與定制技巧 在數據可視化中&#xff0c;條形圖是最常用的圖表類型之一&#xff0c;它能夠清晰地展示不同類別之間的數量對比。今天&#xff0c;我們將繼續學習如何使用pyecharts創建5種不同風格的條形圖。pyecahts源碼 圖表1&#xff1a;帶…

【C語言】文件操作全解析

文章目錄一、為什么需要文件操作&#xff1f;二、認識文件&#xff1a;不止是磁盤上的存儲2.1 程序文件2.2 數據文件2.3 文件名的構成三、文本文件與二進制文件&#xff1a;數據的兩種形態3.1 存儲方式差異3.2 實例對比&#xff1a;整數10000的存儲3.3 二進制文件操作示例四、文…

C結構體的幾種定義形式 + typedef結合使用的好處

struct 語句定義了一個包含多個成員的新的數據類型&#xff0c;struct 語句的格式如下&#xff1a; struct tag { member-list member-list member-list ... } variable-…

SPICE電容矩陣

SPICE電容矩陣: 如果有許多條傳輸線,就可以用下標來標記每一條線。例如,如果有5條線,就用1~5分別標記,依慣例把返回路徑導體標記為導線0。圖10.6給出了5條導線和一個公共返回平面的橫截面圖。首先研究電容器元件,下一節再討論電感器元件。 在這個線的集合中,每對導線之間…

【Java】棧和隊列

文章目錄1.棧1.1 棧的定義1.2 棧的使用1.3 棧的模擬實現2.隊列2.1 隊列的定義2.2 隊列的使用2.3 隊列的模擬實現3.循環隊列3.1 循環隊列的概念3.2 循環隊列判斷空和滿4.雙端隊列Deque1.棧 1.1 棧的定義 棧是一種特殊的線性表&#xff0c;其只允許在固定的一段進行數據的插入或…

【性能測試】---測試工具篇(jmeter)

目錄 1、安裝并啟動jemeter 2、重點組件 2.1、線程組&#xff1a; 2.2、HTTP取樣器?編輯 2.3、查看結果樹 2.4、HTTP請求默認值 2.5、HTTP信息頭管理器 2.6、JSON提取器 2.7、JSON斷言 2.8、同步定時器 2.9、CSV數據文件設置 2.10、HTTP Cookie管理器 3、測試報告…

機器學習(12):拉索回歸Lasso

- 拉索回歸可以將一些權重壓縮到零&#xff0c;從而實現特征選擇。這意味著模型最終可能只包含一部分特征。 - 適用于特征數量遠大于樣本數量的情況&#xff0c;或者當特征間存在相關性時&#xff0c;可以從中選擇最相關的特征。 - 拉索回歸產生的模型可能更簡單&#xff0c;因…

Redis持久化存儲

Redis持久化存儲詳解 一、核心持久化機制 Redis提供兩種主要持久化方式&#xff1a;RDB&#xff08;快照&#xff09; 和 AOF&#xff08;追加文件&#xff09;&#xff0c;以及兩者的混合模式。 RDB&#xff08;Redis Database&#xff09;快照持久化 工作原理 RDB通過創建數據…

python學智能算法(三十四)|SVM-KKT條件回顧

【1】引言 前序學習進程中&#xff0c;對軟邊界拉格朗日方程進行了初步構建。 其中約定了兩個拉格朗日乘子要非負&#xff0c;其本質是要滿足KKT條件。 今天就乘此次機會&#xff0c;在回顧一下KKT條件。 【2】定義 當問題無約束的時候&#xff0c;只要讓函數梯度為零&#…

【網絡基礎】計算機網絡發展背景及傳輸數據過程介紹

本文旨在幫助初學者建立起計算機網絡的基礎認知&#xff0c;從網絡的發展背景到網絡協議的分層模型&#xff0c;再到IP與MAC地址的基本概念&#xff0c;全面覆蓋第一階段學習重點。 &#x1f4cc; 本節重點 了解計算機網絡的發展背景&#xff0c;掌握局域網&#xff08;LAN&am…

阿里云-通義靈碼:解鎖云原生智能開發新能力,讓云開發更“靈”~

免責聲明&#xff1a;此篇文章所有內容皆是本人實驗&#xff0c;并非廣告推廣&#xff0c;并非抄襲&#xff0c;如有侵權&#xff0c;請聯系筆者。 每日一句 信念其實就是相信未來&#xff0c; 相信內在&#xff0c; 以及坦然美好的心境。 目錄 每日一句 一. 引言 二.通義…

lesson33:Python協程詳解:從原理到實戰的異步編程指南

目錄 一、協程核心概念&#xff1a;輕量級并發的本質 1.1 什么是協程&#xff1f; 1.2 協程與線程/進程的對比 二、協程工作原理&#xff1a;事件循環與協作式調度 2.1 事件循環&#xff08;Event Loop&#xff09;&#xff1a;協程的"調度中心" 2.2 協作式調度…

深入理解C++模板進階:非類型參數、特化與分離編譯

前言C模板是泛型編程的核心&#xff0c;它允許我們編寫與類型無關的代碼。在掌握了模板的基礎知識后&#xff0c;我們需要進一步了解模板的高級特性&#xff0c;以便更靈活地使用它們。本文將深入探討三個重要的模板進階主題&#xff1a;非類型模板參數、模板特化以及模板的分離…

使用winsw把SpringBoot項目注冊成window服務

目錄 一、使用winsw注冊 1.1、項目打jar包 1.2、下載winsw 1.3、把 WinSW.NET4.exe 重新命名 1.4、編寫m配置文件用于配置注冊信息 1.5、創建文件夾存放你的文件 1.6、安裝服務 1.7、啟動服務 1.8、卸載服務 1.8、停止服務 一、使用winsw注冊 1.1、項目打jar包 例如項目jar包名…

進階向:Python開發簡易QQ聊天機器人

數字化時代的聊天機器人應用在當今數字化時代&#xff0c;聊天機器人已經成為日常生活和商業活動中不可或缺的一部分。根據市場研究數據顯示&#xff0c;全球聊天機器人市場規模預計將在2026年達到102億美元&#xff0c;年復合增長率達到34.75%。這些智能助手正廣泛應用于以下場…

基于開源鏈動2+1模式AI智能名片S2B2C商城小程序的用戶留存策略研究

摘要&#xff1a;在數字化商業競爭白熱化的當下&#xff0c;用戶留存成為企業可持續發展的核心命題。本文聚焦開源鏈動21模式AI智能名片S2B2C商城小程序這一創新技術組合&#xff0c;通過分析其技術架構、模式創新與生態閉環的協同效應&#xff0c;揭示其在降低用戶決策成本、提…

單詞的劃分(動態規劃)

題目描述有一個很長的由小寫字母組成字符串。為了便于對這個字符串進行分析&#xff0c;需要將它劃分成若干個部分&#xff0c;每個部分稱為一個單詞。出于減少分析量的目的&#xff0c;我們希望劃分出的單詞數越少越好。你就是來完成這一劃分工作的。輸入第一行&#xff0c;一…

C語言學習筆記——文件

目錄1 文件的概念2 程序文件和數據文件3 二進制文件和文本文件4 流4.1 流的概念4.2 標準流5 文件信息區和文件指針6 處理文件的庫函數6.1 fopen6.2 fclose6.3 fgetc6.4 fputc6.5 fgets6.6 fputs6.7 fscanf6.8 fprintf6.9 fread6.10 fwrite6.11 fseek6.12 ftell6.13 rewind6.14 …

C++語法與面向對象特性(2)

一.inline函數1.inline的基本特性被inline修飾的函數被稱為內聯函數。inline函數設計的初衷是為了優化宏的功能&#xff0c;編譯器會在編譯階段對inline函數進行展開。然而需要注意的是&#xff0c;inline對于編譯器而言是一種建議&#xff0c;它通常會展開一些簡短的&#xff…