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

时间: 2024-12-20 01:05:24

View.requestLayout的相关文章

android自定义View (五)view.requestLayout() 与 invalidate()

一.要点 If in the course of processing the event, the view's bounds may need to be changed, the view will call requestLayout(). Similarly, if in the course of processing the event the view's appearance may need to be changed, the view will call invalida

Android View 深度分析requestLayout、invalidate与postInvalidate

requestLayout 当我们动态移动一个View的位置,或者View的大小.形状发生了变化的时候,我们可以在view中调用这个方法,即: 1 view.requestLayout(); 那么该方法的作用是什么呢? 从方法名字可以知道,“请求布局”,那就是说,如果调用了这个方法,那么对于一个子View来说,应该会重新进行布局流程.但是,真实情况略有不同,如果子View调用了这个方法,其实会从View树重新进行一次测量.布局.绘制这三个流程,最终就会显示子View的最终情况.那么,这个方法是怎

Android中View绘制流程分析

创建Window 在Activity的attach方法中通过调用PolicyManager.makeNewWindo创建Window,将一个View add到WindowManager时,WindowManagerImpl创建一个ViewRoot来管理该窗口的根View.并通过ViewRoot.setView方法把该View传给ViewRoot. final void attach(Context context, ActivityThread aThread, Instrumentation

android 用java代码设置布局、视图View的宽度/高度或自适应

在achat项目中,对话内容的长宽设置为自适应,但是如果文本内容太多,则宽度差不多布满,若自己说的和对方说的都很多内容,则满屏都是文字,则不容易分辨出是来自别人说的还是自己说的.那么需要对本身具有自适应的布局再来个宽度限制. 首先,在xml里面定义layout的layout_width/layout_height都要设置为wrap_content,然后在getView方法里面,一开始就让这个布局wrap_content(为什么xml里面设置了wrap_content,这里还要再设置一遍呢?因为布

如果写一个点击view带动画的下滑展开显示隐藏内容的控件

原理是在onMeasure中得到隐藏内容的高度,点击这个view的时候对隐藏的view startAnimation,让它的高度从0增长到onMeasure得到的这个View的measureHeight 具体这样写: public class ExpandableLayout extends LinearLayout { private Context mContext; private LinearLayout mHandleView; private RelativeLayout mCont

长谈:关于 View Measure 测量机制,让我一次把话说完

<倚天屠龙记中>有这么一处:张三丰示范自创的太极剑演示给张无忌看,然后问他记住招式没有.张无忌说记住了一半.张三丰又慢吞吞使了一遍,问他记住多少,张无忌说只记得几招了.张三丰最后又示范了一遍,张无忌想了想说,这次全忘光了.张三丰很满意,于是放心让张无忌与八臂神剑去比试. 首先声明,这一篇篇幅很长很长很长的文章.目的就是为了把 Android 中关于 View 测量的机制一次性说清楚.算是自己对自己较真.写的时候花了好几天,几次想放弃,想放弃的原因不是我自己没有弄清楚,而是觉得自己叙事脉络已经紊

深入理解 Android 之 View 的绘制流程

概述 本篇文章会从源码(基于Android 6.0)角度分析Android中View的绘制流程,侧重于对整体流程的分析,对一些难以理解的点加以重点阐述,目的是把View绘制的整个流程把握好,而对于特定实现细节则可以日后再对相应源码进行研读.在进行实际的分析之前,我们先来看下面这张图: 我们来对上图做出简单解释:DecorView是一个应用窗口的根容器,它本质上是一个FrameLayout.DecorView有唯一一个子View,它是一个垂直LinearLayout,包含两个子元素,一个是Titl

View绘制过程详解

View绘制过程详解 界面窗口的根布局是DecorView,该类继承自FrameLayout.说到View绘制,想到的就是从这里入手,而FrameLayout继承自ViewGroup.感觉绘制肯定会在ViewGroup或者View中, 但是木有找到.发现ViewGroup实现ViewParent接口,而ViewParent有一个实现类是ViewRootImpl, ViewGruop中会使用ViewRootImpl- /** * The top of a view hierarchy, imple

源码分析篇 - Android绘制流程(三)requestLayout()与invalidate()流程分析

本文主要探讨能够触发performTraversals()执行的invalidate().postInvalidate()和requestLayout()方法的流程.invalidate()和postInvalidate()能够触发View的重画,这两个方法最终会调用到performTraversals()中的performDraw()来完成重绘制,但是是否会执行onMeasure()和onLayout()过程要根据标志位的状况来决定:requesetLayout()方法也会调用到perform