參考:安卓高手之路之圖形系統(6)requestLayout的流程
一、invalidate和postInvalidate
這兩個方法都是在重繪當前控件的時候調用的。invalidate在UI線程中調用,postInvalidate在非UI線程中調用。因為android的UI線程是非線程安全的,所以在非UI線程中,需要使用postInvalidate來使View重繪。當然,Handler和invalidate配合使用,也可以達到目的,但是程序會比較繁瑣。
二、requestLayout
如果View對應的位置已經發生了變化,原來被他覆蓋的內容需要被顯示出來。這時,如果只重新繪制當前View,已經起不到效果。這就需要requestLayout來實現重繪當前View和父View,甚至更上層的View,以此類推。
下面附上上面博客的關於requestLayout的調用過程的內容:
當一個View調用requestLayout的時候,會給當前的View設置一個FORCE_LAYOUT標記。由此向ViewParent請求布局。這樣從這個View開始向上一直requestLayout。最終到達ViewRootImpl。ViewParent 就是當前的傳輸鏈。【參見職責鏈設計模式】
第一步。
ViewRootImpl發現請求了布局。那么就會調用measure方法。
measure方法確認當前View是否有FORCE_LAYOUT標記。
如果有,那么就會進行重新measure。並且設置標記LAYOUT_REQUIRED。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag mPrivateFlags &= ~MEASURED_DIMENSION_SET; if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE); } // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) { throw new IllegalStateException("onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; }
第二步。
在隨后的layout方法中,會判斷這個標記。如果這個標記為true。
那么就一定會調用onLayout.
onLayout調用后清理LAYOUT_REQUIRED標記。
layout調用之后,會清理掉FORCE_LAYOUT標記。
當然在上述過程中,影響到了兄弟或者是父親View的大小, 那么也兄弟或者是父親View也會調用layout/onLayout。不管其是否已經調用requestLayout。如果說指定的MeasureSpec為此也發生了變化,
那么measure/onMeasure也會被調用。
通過上述分析發現,只要調用了requestlayout, 那么measure和onMeasure,以及layout,onlayout,draw onDraw都會被調用。
在很多情況下,requestLayout是不需要被調用的。例如,我們把一個AbsoluteLayout里面的childView挪動一下位置。我們僅僅需要調用的可能就是重新布局當前AbsoluteLayout,然后調用invalidate方法進行重繪。而不是從當前View向上的整個View樹形結構都要重新layout,onLayout,measure,onMeasure一次。
這個時候,怎么辦?
一種方法是,直接調用onLayout。然后調用invalidate進行重繪。很明顯可以提升繪制效率。由於父View的layout實現中對會通知布局的listener。但是由於無法得到listener,因此調用onlayout的時候無法對其進行通知,這也是這種實現的缺陷。
