View生命週期相關方法
View是什麼?官方源碼註釋中的定義:這個類是用戶接口的基礎構件。View表示屏幕上的一塊矩形區域,負責繪製這個區域和事件處理。
View是所有widget類的基類,Widget類用於創建交互式UI構件(按鈕,輸入框等)。
View類的ViewGroup子類是layout的基類,Layout是一個不可見的容器,它保存着View(或ViewGroup)並定義這些View的layout 屬性。
簡單點說,View就是屏幕上的一塊矩形區域,我們可以在這塊區域繪製我們想讓用戶看到的圖形。
關於View的生命週期,官方源碼註釋中有詳細的描述,作爲英語六級勉強飄過的選手,我頂着巨大的壓力翻譯了一下。
- Creation(創建)
- Constructors(構造函數):有一種形式的構造函數會在View在代碼中被創建時調用,另一種形式的構造函數會在View從layout加載出來時被調用。
第二種形式的構造函數會解析和應用layout文件中定義的任何屬性。 - onFinishInflate():該方法當View及其子View從XML文件中加載完成後會被調用。
- Constructors(構造函數):有一種形式的構造函數會在View在代碼中被創建時調用,另一種形式的構造函數會在View從layout加載出來時被調用。
- Layout(佈局)
- onMeasure(int, int):該方法在計算當前View及其所有子View尺寸大小需求時會被調用。
- onLayout(boolean, int, int, int, int):該方法在當前View需要爲其子View分配尺寸和位置時會被調用。
- onSizeChanged(int, int, int, int):該方法在當前View尺寸變化時被調用。
- Drawing(繪製)
- onDraw(android.graphics.Canvas):該方法在當前View需要呈現其內容時被調用。
- Event processing(事件處理)
- onKeyDown(int, KeyEvent):該方法在一個物理按鍵事件發生時被調用。
- onKeyUp(int, KeyEvent):該方法在一個物理按鍵彈起事件發生時被調用。
- onTrackballEvent(MotionEvent):該方法在一個軌跡球運動事件發生時被調用。
- onTouchEvent(MotionEvent):該方法在一個觸摸屏幕運動事件發生時被調用。
- Focus(聚焦)
- onFocusChanged(boolean, int, android.graphics.Rect):該方法在當前View獲得或失去焦點時被調用。
- onWindowFocusChanged(boolean):該方法在包含當前View的window獲得或失去焦點時被調用。
- Attaching(附上)
- onAttachedToWindow():該方法在當前View被附到一個window上時被調用。
- onDetachedFromWindow():該方法在當前View從一個window上分離時被調用。
- onVisibilityChanged(View, int):該方法在當前View或其祖先的可見性改變時被調用。
- onWindowVisibilityChanged(int):該方法在包含當前View的window可見性改變時被調用。
上述方法是View生命週期中涉及到的比較重要的一部分,View類中包含了很多的方法和屬性,有興趣的話各位可以自己研究一下。
View生命週期相關方法調用順序
簡單的瞭解了View生命週期相關的幾個方法,接着我們看看這些方法調用的順序是怎樣的,我們針對View的可見性分三種情況來觀察。
- android:visibility=visible
- 創建
I/TestView: TestView(Context context, AttributeSet attrs) I/TestView: onFinishInflate() I/TestView: onVisibilityChanged(View changedView, int visibility) changedView = com.android.internal.policy.impl.PhoneWindow$DecorView{2192bad9 I.E..... R.....ID 0,0-0,0} visibility = 4 I/TestView: onVisibilityChanged(View changedView, int visibility) changedView = com.android.internal.policy.impl.PhoneWindow$DecorView{2192bad9 V.E..... R.....ID 0,0-0,0} visibility = 0 I/TestView: onAttachedToWindow() I/TestView: onWindowVisibilityChanged(int visibility) visibility = 0 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743848 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743848 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073744016 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073744016 I/TestView: onSizeChanged(int w, int h, int oldw, int oldh) w = 1328 h = 2192 oldw = 0 oldh0 I/TestView: onLayout(boolean changed, int left, int top, int right, int bottom) changed = true left = 56 top = 56 right = 1384 bottom = 2248 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743820 I/TestView: onSizeChanged(int w, int h, int oldw, int oldh) w = 1328 h = 1996 oldw = 1328 oldh2192 I/TestView: onLayout(boolean changed, int left, int top, int right, int bottom) changed = true left = 56 top = 56 right = 1384 bottom = 2052 I/TestView: onDraw(Canvas canvas) I/TestView: onWindowFocusChanged(boolean hasWindowFocus) hasWindowFocus = true I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743820 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743820 I/TestView: onLayout(boolean changed, int left, int top, int right, int bottom) changed = false left = 56 top = 56 right = 1384 bottom = 2052 I/TestView: onDraw(Canvas canvas)
- 銷燬
I/TestView: onWindowFocusChanged(boolean hasWindowFocus) hasWindowFocus = false I/TestView: onWindowVisibilityChanged(int visibility) visibility = 8 I/TestView: onDetachedFromWindow()
- 創建
- android:visibility=invisible
- 創建
I/TestView: onVisibilityChanged(View changedView, int visibility) changedView = com.example.junyizhou.rxjavademo.TestView{3ead3d52 I.ED.... ........ 0,0-0,0} visibility = 4 I/TestView: TestView(Context context, AttributeSet attrs) I/TestView: onFinishInflate() I/TestView: onVisibilityChanged(View changedView, int visibility) changedView = com.android.internal.policy.impl.PhoneWindow$DecorView{3aeb2b95 I.E..... R.....ID 0,0-0,0} visibility = 4 I/TestView: onVisibilityChanged(View changedView, int visibility) changedView = com.android.internal.policy.impl.PhoneWindow$DecorView{3aeb2b95 V.E..... R.....ID 0,0-0,0} visibility = 0 I/TestView: onAttachedToWindow() I/TestView: onWindowVisibilityChanged(int visibility) visibility = 0 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743848 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743848 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073744016 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073744016 I/TestView: onSizeChanged(int w, int h, int oldw, int oldh) w = 1328 h = 2192 oldw = 0 oldh0 I/TestView: onLayout(boolean changed, int left, int top, int right, int bottom) changed = true left = 56 top = 56 right = 1384 bottom = 2248 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743820 I/TestView: onSizeChanged(int w, int h, int oldw, int oldh) w = 1328 h = 1996 oldw = 1328 oldh2192 I/TestView: onLayout(boolean changed, int left, int top, int right, int bottom) changed = true left = 56 top = 56 right = 1384 bottom = 2052 I/TestView: onWindowFocusChanged(boolean hasWindowFocus) hasWindowFocus = true I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743820 I/TestView: onMeasure(int widthMeasureSpec, int heightMeasureSpec) widthMeasureSpec = 1073743152 heightMeasureSpec = 1073743820 I/TestView: onLayout(boolean changed, int left, int top, int right, int bottom) changed = false left = 56 top = 56 right = 1384 bottom = 2052
- 銷燬
I/TestView: onWindowFocusChanged(boolean hasWindowFocus) hasWindowFocus = false I/TestView: onWindowVisibilityChanged(int visibility) visibility = 8 I/TestView: onDetachedFromWindow()
- 創建
- android:visibility=gone
- 創建
I/TestView: onVisibilityChanged(View changedView, int visibility) changedView = com.example.junyizhou.rxjavademo.TestView{3ead3d52 G.ED.... ......I. 0,0-0,0} visibility = 8 I/TestView: TestView(Context context, AttributeSet attrs) I/TestView: onFinishInflate() I/TestView: onVisibilityChanged(View changedView, int visibility) changedView = com.android.internal.policy.impl.PhoneWindow$DecorView{3aeb2b95 I.E..... R.....ID 0,0-0,0} visibility = 4 I/TestView: onVisibilityChanged(View changedView, int visibility) changedView = com.android.internal.policy.impl.PhoneWindow$DecorView{3aeb2b95 V.E..... R.....ID 0,0-0,0} visibility = 0 I/TestView: onAttachedToWindow() I/TestView: onWindowVisibilityChanged(int visibility) visibility = 0 I/TestView: onWindowFocusChanged(boolean hasWindowFocus) hasWindowFocus = true
- 銷燬
I/TestView: onWindowFocusChanged(boolean hasWindowFocus) hasWindowFocus = false I/TestView: onWindowVisibilityChanged(int visibility) visibility = 8 I/TestView: onDetachedFromWindow()
- 創建
總結
從中我們可以看出:
- View默認爲可見的,不是默認值時先調用onVisibilityChanged(),但是此時該View的尺寸、位置等信息都不知道。
- 可見性改變後纔是調用帶有兩個參數的構造函數,當然,如果該View不是在layout中定義的話,會調用一個參數的構造函數。
- 從XMl文件中inflate完成(onFinishInflate())。
- 將View加到window中(View是gone的,那麼View創建生命週期也就結束)。
- 測量view的長寬(onMeasure())。
- 定位View 在父View中的位置(onLayout()),若View是invisible,則View的創建生命週期結束。
- 繪製View的content(onDraw()),只有可見的View纔在window中繪製。
- View的銷燬流程和可見性沒有關係。
綜上所述:View的關鍵生命週期爲:
[改變可見性] --> 構造View() --> onFinishInflate() --> onAttachedToWindow() --> onMeasure() --> onSizeChanged() --> onLayout() --> onDraw() --> onDetackedFromWindow()
自定義View時我們不可避免的要和View生命週期相關函數打交道,可能需要重新其中的某個或某幾個來滿足定製的需求,因此瞭解View的生命週期是Android程序猿進階的必經之路。當然,我們沒必要重新所有的方法,如果我們只是單純的想把一個Bitmap畫到View上,那我們只要重寫View的onDraw方法就可以了,事實上自定義View的大部分情況我們也只是關注這個方法。
原文鏈接:http://www.jianshu.com/p/08e6dab7886e
著作權歸作者所有,轉載請聯繫作者獲得授權,並標註“簡書作者”。