android-進階(3)-自定義view(2)-Android中View繪製流程以及相關方法的分析
最近正在學自定義view,這篇文章主要講view的繪製流程和一些相關的方法,淺顯易懂,寫的非常好,忍不住就轉載了。
前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。同時真摯地向渴望瞭解
Android 框架層的網友,推薦這本書,希望你們能夠在Android開發裡學到更多的知識 。
整個View樹的繪圖流程是在ViewRoot.java類的performTraversals()函式展開的,該函式做的執行過程可簡單概況為
根據之前設置的狀態,判斷是否需要重新計算檢視大小(measure)、是否重新需要安置檢視的位置(layout)、以及是否需要重繪
(draw),其框架過程如下:
步驟其實為host.layout()
接下來溫習一下整個View樹的結構,對每個具體View物件的操作,其實就是個遞迴的實現。
關於這個 DecorView 根檢視的說明,可以參考我的這篇部落格:
流程一: mesarue()過程
主要作用:為整個View樹計算實際的大小,即設定實際的高(對應屬性:mMeasuredHeight)和寬(對應屬性:
mMeasureWidth),每個View的控制元件的實際寬高都是由父檢視和本身檢視決定的。
具體的呼叫鏈如下:
ViewRoot根物件地屬性mView(其型別一般為ViewGroup型別)呼叫measure()方法去計算View樹的大小,回撥
View/ViewGroup物件的onMeasure()方法,該方法實現的功能如下:
1、設定本View檢視的最終大小,該功能的實現通過呼叫setMeasuredDimension()方法去設定實際的高(對應屬性:
mMeasuredHeight)和寬(對應屬性:mMeasureWidth) ;
2 、如果該View物件是個ViewGroup型別,需要重寫該onMeasure()方法,對其子檢視進行遍歷的measure()過程。
2.1 對每個子檢視的measure()過程,是通過呼叫父類ViewGroup.java類裡的measureChildWithMargins()方法去
實現,該方法內部只是簡單地呼叫了View物件的measure()方法。(由於measureChildWithMargins()方法只是一個過渡
層更簡單的做法是直接呼叫View物件的measure()方法)。
整個measure呼叫流程就是個樹形的遞迴過程
measure函式原型為 View.java 該函式不能被過載
[java] view plaincopyprint?- publicfinalvoid measure(int widthMeasureSpec, int heightMeasureSpec) {
- //....
- //回撥onMeasure()方法
- onMeasure(widthMeasureSpec, heightMeasureSpec);
- //more
- }
為了大家更好的理解,採用“二B程式設計師”的方式利用虛擬碼描述該measure流程
- //回撥View視圖裡的onMeasure過程
- privatevoid onMeasure(int height , int width){
- //設定該view的實際寬(mMeasuredWidth)高(mMeasuredHeight)
- //1、該方法必須在onMeasure呼叫,否者報異常。
- setMeasuredDimension(h , l) ;
- //2、如果該View是ViewGroup型別,則對它的每個子View進行measure()過程
- int childCount = getChildCount() ;
- for(int i=0 ;i<childCount ;i++){
- //2.1、獲得每個子View物件引用
- View child = getChildAt(i) ;
- //整個measure()過程就是個遞迴過程
- //該方法只是一個過濾器,最後會呼叫measure()過程 ;或者 measureChild(child , h, i)方法都
- measureChildWithMargins(child , h, i) ;
- //其實,對於我們自己寫的應用來說,最好的辦法是去掉框架裡的該方法,直接呼叫view.measure(),如下:
- //child.measure(h, l)
- }
- }
- //該方法具體實現在ViewGroup.java裡 。
- protectedvoid measureChildWithMargins(View v, int height , int width){
- v.measure(h,l)
- }
流程二、 layout佈局過程:
主要作用 :為將整個根據子檢視的大小以及佈局引數將View樹放到合適的位置上。
具體的呼叫鏈如下:
host.layout()開始View樹的佈局,繼而回調給View/ViewGroup類中的layout()方法。具體流程如下
1 、layout方法會設定該View檢視位於父檢視的座標軸,即mLeft,mTop,mLeft,mBottom(呼叫setFrame()函式去實現)
接下來回調onLayout()方法(如果該View是ViewGroup物件,需要實現該方法,對每個子檢視進行佈局) ;
2、如果該View是個ViewGroup型別,需要遍歷每個子檢視chiildView,呼叫該子檢視的layout()方法去設定它的座標值。
layout函式原型為 ,位於View.java
[java] view plaincopyprint?- /* final 識別符號 , 不能被過載 , 引數為每個檢視位於父檢視的座標軸
- * @param l Left position, relative to parent
- * @param t Top position, relative to parent
- * @param r Right position, relative to parent
- * @param b Bottom position, relative to parent
- */
- publicfinalvoid layout(int l, int t, int r, int b) {
- boolean changed = setFrame(l, t, r, b); //設定每個檢視位於父檢視的座標軸
- if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
- }
- onLayout(changed, l, t, r, b);//回撥onLayout函式 ,設定每個子檢視的佈局
- mPrivateFlags &= ~LAYOUT_REQUIRED;
- }
- mPrivateFlags &= ~FORCE_LAYOUT;
- }
同樣地, 將上面layout呼叫流程,用虛擬碼描述如下:
[java] view plaincopyprint?
- // layout()過程 ViewRoot.java
- // 發起layout()的"發號者"在ViewRoot.java裡的performTraversals()方法, mView.layout()
- privatevoid performTraversals(){
- //...
- View mView ;
- mView.layout(left,top,right,bottom) ;
- //....
- }
- //回撥View視圖裡的onLayout過程 ,該方法只由ViewGroup型別實現
- privatevoid onLayout(int left , int top , right , bottom){
- //如果該View不是ViewGroup型別
- //呼叫setFrame()方法設定該控制元件的在父檢視上的座標軸
- setFrame(l ,t , r ,b) ;
- //--------------------------
- //如果該View是ViewGroup型別,則對它的每個子View進行layout()過程
- int childCount = getChildCount() ;
- for(int i=0 ;i<childCount ;i++){
- //2.1、獲得每個子View物件引用
- View child = getChildAt(i) ;
- //整個layout()過程就是個遞迴過程
- child.layout(l, t, r, b) ;
- }
- }
流程三、 draw()繪圖過程
由ViewRoot物件的performTraversals()方法呼叫draw()方法發起繪製該View樹,值得注意的是每次發起繪圖時,並不
會重新繪製每個View樹的檢視,而只會重新繪製那些“需要重繪”的檢視,View類內部變數包含了一個標誌位DRAWN,當該
檢視需要重繪時,就會為該View新增該標誌位。
呼叫流程 :
mView.draw()開始繪製,draw()方法實現的功能如下:
1 、繪製該View的背景
2 、為顯示漸變框做一些準備操作(見5,大多數情況下,不需要改漸變框)
3、呼叫onDraw()方法繪製檢視本身 (每個View都需要過載該方法,ViewGroup不需要實現該方法)
4、呼叫dispatchDraw ()方法繪製子檢視(如果該View型別不為ViewGroup,即不包含子檢視,不需要過載該方法)
值得說明的是,ViewGroup類已經為我們重寫了dispatchDraw ()的功能實現,應用程式一般不需要重寫該方法,但可以過載父類
函式實現具體的功能。
4.1 dispatchDraw()方法內部會遍歷每個子檢視,呼叫drawChild()去重新回撥每個子檢視的draw()方法(注意,這個
相關推薦
android-進階(3)-自定義view(2)-Android中View繪製流程以及相關方法的分析
最近正在學自定義view,這篇文章主要講view的繪製流程和一些相關的方法,淺顯易懂,寫的非常好,忍不住就轉載了。 前言: 本文是我讀《Android核心剖析》第13章----View工作原理總結而成的,在此膜拜下作者 。
android-進階(3)-自定義view(1)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:custom="http://sch
vue進階(1) ---自定義元件
vue自定義元件 1、區域性元件,區域性元件必須要手動掛載,不然無法生效 2、全域性元件,全域性元件不需要手動掛載,但是不常用,儘量不要在全域性上掛載變數或者元件(可能會影響瀏覽器效能) 3、配合
Python進階(3)_進程與線程中的lock(互斥鎖、遞歸鎖、信號量)
fun 我們 bsp 控制 支持 發生 class 線程 數據操作 1、同步鎖 (Lock) 當各個線程需要訪問一個公共資源時,會出現數據紊亂 例如: 1 import threading,time 2 def sub(): 3 global num
Senparc.Weixin微信開發(3) 自定義菜單與獲取用戶組
開發 分享圖片 獲取 local lock 自定義 oba summary setting 自定義菜單 代碼參考:http://www.cnblogs.com/szw/p/3750517.html 還可以使用他們官網的自定義:https://neuchar.senparc.
《Android 進階(五)》應用分享簡單實現-LazyApkShare
LazyApkShare 分享當前應用的簡單實現。 開源地址 LazyApkShare 新增依賴 Gradle 步驟一. 根目錄下build.gradle allprojects { repositories { maven { url
python進階(3)——檔案
開啟檔案:(非當前目錄需指定完整路徑) f = open('lcctry.py') 讀取和寫入: f = open('lcctry.txt','w') f.write('hello, world') Out[130]: 12 f.close() 讀取時的r可以不寫,呼叫open時
Senparc.Weixin微信開發(3) 自定義選單與獲取使用者組
自定義選單 程式碼參考:http://www.cnblogs.com/szw/p/3750517.html 還可以使用他們官網的自定義:https://neuchar.senparc.com/User/WeixinMenu 註冊好,再先弄好配置。直接可以推送。 獲取使用者組 /// &
shiro學習筆記(3)--自定義realm、授權
一:自定義Realm 1、繼承AuthorizingRealm(因為該類中有認證、授權的抽象方法,實現簡單) public class MyRealm1 extends AuthorizingRealm{ @Override public String getName(
微信開發學習總結(四)——自定義選單(3)——自定義選單刪除介面
自定義選單刪除介面 使用介面建立自定義選單後,開發者還可使用介面刪除當前使用的自定義選單。另請注意,在個性化選單時,呼叫此介面會刪除預設選單及全部個性化選單。 請求說明 http請求方式:GET https://api.weixin.qq.com/cgi-bin/menu/d
Android進階(二): 應用程序啟動過程
1.前言 最近一直在看 《Android進階解密》 的一本書,這本書編寫邏輯、流程都非常好,而且很容易看懂,非常推薦大家去看看(沒有收廣告費,單純覺得作者寫的很好)。 今天就將 應用程序啟動過程 總結一下(基於Android 8.0 系統)。 文章中例項&nbs
Android進階(一): Launcher啟動過程
1.前言 最近一直在看 《Android進階解密》 的一本書,這本書編寫邏輯、流程都非常好,而且很容易看懂,非常推薦大家去看看(沒有收廣告費,單純覺得作者寫的很好)。 今天就將 Launcher 系統啟動過程 總結一下(基於Android 8.0 系統)。 文章
Android進階(三):Application啟動過程(最詳細&最簡單)
1.前言 最近一直在看 《Android進階解密》 的一本書,這本書編寫邏輯、流程都非常好,而且很容易看懂,非常推薦大家去看看(沒有收廣告費,單純覺得作者寫的很好)。 上一篇簡單的介紹了Android進階(二): 應用程序啟動過程,最終知道了ActivityThrea
Android進階(四):Activity啟動過程(最詳細&最簡單)
1.前言 最近一直在看 《Android進階解密》 的一本書,這本書編寫邏輯、流程都非常好,而且很容易看懂,非常推薦大家去看看(沒有收廣告費,單純覺得作者寫的很好)。 上一篇簡單的介紹了Android進階(三):Application啟動過程(最詳細&最簡單)
【我的Android進階之旅】自定義ContentProvider
引言 我們知道Android有四大元件,ContentProvider是其中之一,顧名思義:內容提供者。什麼是內容提供者呢?一個抽象類,可以暴露應用的資料給其他應用。應用裡的資料通常說的是資料庫,事實上普通的檔案,甚至是記憶體中的物件,也可以作為內容提供者暴露的
最想知道的git操作系列(3)---自定義diff和merge工具(winmerge)
使用git的時候覺得自帶的merge工具不是很順手,同時也習慣winmerge比對工具,這裡mark一下,如何切換使用winmerge 下載安裝就不說了,一切預設安裝即可 這裡說明一下如何配置winmerge 1、配置merge工具 git config --glo
【我的Android進階之旅】自定義控制元件之使用ViewPager實現可以預覽的畫廊效果,並且自定義畫面切換的動畫效果的切換時間
我們來看下效果 在這裡,我們實現的是,一個ViewPager來顯示圖片列表。這裡一個頁面,ViewPage展示了前後的預覽,我們讓預覽頁進行Y軸的壓縮,並設定透明度為0.5f,所有我們看到gif最後,左右兩邊的圖片有點朦朧感。讓預覽頁和主頁面有主從感。我們用分
類的學習筆記(3)——自定義裝飾器及裝飾器的理解
例一: 實現多加100 def fun1(x): def fun2(y): return x(y)+100 return fun2 #裝飾器 def ff(y): return y*y
spring-boot整合redis作為快取(3)——自定義key
分幾篇文章總結spring-boot與Redis的整合 4、自定義key 5、spring-boot引入Redis 在上一篇文章中說道key是用來分辨同一個快取中的快取資料的。key是可以自己制定的,也
Android 面試題總結之Android 進階(二)
Android 之美 從0到1 之Android 進階(二) 在上一章節中《Android 之美 從0到1 之Android 進階(一)》中我們已經理解了一些View的基本知識並且知道如何自定義View。那麼本章節將繼續深入理解View,關於View的繪