View.requestLayout


 

參考:安卓高手之路之圖形系統(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標記。

  @SuppressWarnings({"unchecked"})  
   public void layout(int l, int t, int r, int b) {  
       int oldL = mLeft;  
       int oldT = mTop;  
       int oldB = mBottom;  
       int oldR = mRight;  
       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);  
           mPrivateFlags &= ~LAYOUT_REQUIRED;  
  
           ListenerInfo li = mListenerInfo;  
           if (li != null && li.mOnLayoutChangeListeners != null) {  
               ArrayList<OnLayoutChangeListener> listenersCopy =  
                       (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();  
               int numListeners = listenersCopy.size();  
               for (int i = 0; i < numListeners; ++i) {  
                   listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);  
               }  
           }  
       }  
       mPrivateFlags &= ~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的時候無法對其進行通知,這也是這種實現的缺陷。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM