您的位置:首页 > 产品设计 > UI/UE

View.requestLayout()

2016-06-14 09:43 363 查看
View.requestLayout()

当一个View需要parent view重新调用他的onMeasure onLayout来对重新设置自己位置时,特别是当你修改了view的layoutparameter,它的值还没能应用到view上,调用这个方法就可以了,但是要注意parent
view的onLayout是怎么实现的。

requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent
view重新调用他的onMeasure onLayout来对重新设置自己位置。
特别的当view的layoutparameter发生改变,并且它的值还没能应用到view上,这时候适合调用这个方法。

Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree.

我们可以理解为重新布局了一下view;

用途:有时我们在改变一个view 的内容之后可能会造成显示出现错误,比如写ListView的时候 重用convertview中的某个TextView 可能因为前后填入的text长度不同而造成显示出错,此时我们可以在改变内容之后调用requestLayout方法加以解决。

Invalidate和postInvalidate和requestLayout都是用来刷新view控件的:

主线程可以直接调用Invalidate()方法刷新。

子线程可以直接调用postInvalidate()方法刷新。

API的描述是这样的 : Invalidatethe whole view. If the view is visible, onDraw(Canvas) will be called at somepoint in the future. This must be called from a UI thread. To call from anon-UI thread, call postInvalidate().。当Invalidate()被调用的时候,View的OnDraw()就会被调用,Invalidate()必须是在UI线程中被调用,如果在新线程中更新视图的就调用postInvalidate()。

View.requestLayout() 请求重新布局,重新调用:onMeasure,onLayout,onDraw;

View.invalidate()       
刷新视图,相当于调用View.onDraw()方法

第一部分 :   requestLayout的流程

     当一个View调用requestLayout的时候,会给当前的View设置一个FORCE_LAYOUT标记。由此向ViewParent请求布局。这样从这个View开始向上一直requestLayout。最终到达ViewRootImpl。ViewParent 就是当前的传输链。【参见职责链设计模式】

   

    第一步:

     ViewRootImpl发现请求了布局。那么就会调用measure方法。

     measure方法确认当前View是否有FORCE_LAYOUT标记。

     如果有,那么就会进行重新measure。并且设置标记LAYOUT_REQUIRED。

    

Java代码  


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标记。

Java代码  

@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的时候无法对其进行通知,这也是这种实现的缺陷。

 

 第二部分 :  requestLayout和invalidate方法的不同之处

 requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view重新调用他的onMeasure()、 onLayout()来对重新设置自己位置。

特别的当view的layoutparameter发生改变,并且它的值还没能应用到view上,这时候适合调用这个方法。
invalidate:View本身调用迫使view重画。 刷新视图,相当于View.OnDraw()方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息