android view 生命週期

View生命週期相關方法

View是什麼?官方源碼註釋中的定義:這個類是用戶接口的基礎構件。View表示屏幕上的一塊矩形區域,負責繪製這個區域和事件處理。
View是所有widget類的基類,Widget類用於創建交互式UI構件(按鈕,輸入框等)。
View類的ViewGroup子類是layout的基類,Layout是一個不可見的容器,它保存着View(或ViewGroup)並定義這些View的layout 屬性。

簡單點說,View就是屏幕上的一塊矩形區域,我們可以在這塊區域繪製我們想讓用戶看到的圖形。

關於View的生命週期,官方源碼註釋中有詳細的描述,作爲英語六級勉強飄過的選手,我頂着巨大的壓力翻譯了一下。

  1. Creation(創建)
    • Constructors(構造函數):有一種形式的構造函數會在View在代碼中被創建時調用,另一種形式的構造函數會在View從layout加載出來時被調用。
      第二種形式的構造函數會解析和應用layout文件中定義的任何屬性。
    • onFinishInflate():該方法當View及其子View從XML文件中加載完成後會被調用。
  2. Layout(佈局)
    • onMeasure(int, int):該方法在計算當前View及其所有子View尺寸大小需求時會被調用。
    • onLayout(boolean, int, int, int, int):該方法在當前View需要爲其子View分配尺寸和位置時會被調用。
    • onSizeChanged(int, int, int, int):該方法在當前View尺寸變化時被調用。
  3. Drawing(繪製)
    • onDraw(android.graphics.Canvas):該方法在當前View需要呈現其內容時被調用。
  4. Event processing(事件處理)
    • onKeyDown(int, KeyEvent):該方法在一個物理按鍵事件發生時被調用。
    • onKeyUp(int, KeyEvent):該方法在一個物理按鍵彈起事件發生時被調用。
    • onTrackballEvent(MotionEvent):該方法在一個軌跡球運動事件發生時被調用。
    • onTouchEvent(MotionEvent):該方法在一個觸摸屏幕運動事件發生時被調用。
  5. Focus(聚焦)
    • onFocusChanged(boolean, int, android.graphics.Rect):該方法在當前View獲得或失去焦點時被調用。
    • onWindowFocusChanged(boolean):該方法在包含當前View的window獲得或失去焦點時被調用。
  6. Attaching(附上)
    • onAttachedToWindow():該方法在當前View被附到一個window上時被調用。
    • onDetachedFromWindow():該方法在當前View從一個window上分離時被調用。
    • onVisibilityChanged(View, int):該方法在當前View或其祖先的可見性改變時被調用。
    • onWindowVisibilityChanged(int):該方法在包含當前View的window可見性改變時被調用。

上述方法是View生命週期中涉及到的比較重要的一部分,View類中包含了很多的方法和屬性,有興趣的話各位可以自己研究一下。

View生命週期相關方法調用順序

簡單的瞭解了View生命週期相關的幾個方法,接着我們看看這些方法調用的順序是怎樣的,我們針對View的可見性分三種情況來觀察。

  1. 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()
  2. 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()
  3. 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()

總結

從中我們可以看出:

  1. View默認爲可見的,不是默認值時先調用onVisibilityChanged(),但是此時該View的尺寸、位置等信息都不知道。
  2. 可見性改變後纔是調用帶有兩個參數的構造函數,當然,如果該View不是在layout中定義的話,會調用一個參數的構造函數。
  3. 從XMl文件中inflate完成(onFinishInflate())。
  4. 將View加到window中(View是gone的,那麼View創建生命週期也就結束)。
  5. 測量view的長寬(onMeasure())。
  6. 定位View 在父View中的位置(onLayout()),若View是invisible,則View的創建生命週期結束。
  7. 繪製View的content(onDraw()),只有可見的View纔在window中繪製。
  8. 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
著作權歸作者所有,轉載請聯繫作者獲得授權,並標註“簡書作者”。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章