在我们在进行自定义View的相关开发中,当我们更改了当前View的状态,比如大小,位置等,我们需要重新刷新整个界面,保证显示最新的状态。在Android中,让当前的视图重绘有两种方式,invalidate和requestLayout,今天我们看看这两种方式的原理以及区别。

invalidate的原理

  public void invalidate() {
        invalidate(true);

最后会调用到invalidateInternal这个方法

 void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
        if (mGhostView != null) {
            mGhostView.invalidate(true);
            return;
        if (skipInvalidate()) {
            return;
        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
                || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
            if (fullInvalidate) {
                mLastIsOpaque = isOpaque();
                mPrivateFlags &= ~PFLAG_DRAWN;
            mPrivateFlags |= PFLAG_DIRTY;
            if (invalidateCache) {
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            // Propagate the damage rectangle to the parent view.
            final AttachInfo ai = mAttachInfo;
            final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect;
                damage.set(l, t, r, b);
                p.invalidateChild(this, damage);
            .....

我们看到方法的最后调用了ViewParent的invalidateChild方法,因为ViewParent是个接口,invalidateChild是空实现,我们去看看它的实现类ViewRootImpl中的invalidateChild是如何做的

 @Override
    public void invalidateChild(View child, Rect dirty) {
        invalidateChildInParent(null, dirty);
  @Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();
        if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
        if (dirty == null) {
            invalidate();
            return null;
        } else if (dirty.isEmpty() && !mIsAnimating) {
            return null;
        if (mCurScrollY != 0 || mTranslator != null) {
            mTempRect.set(dirty);
            dirty = mTempRect;
            if (mCurScrollY != 0) {
                dirty.offset(0, -mCurScrollY);
            if (mTranslator != null) {
                mTranslator.translateRectInAppWindowToScreen(dirty);
            if (mAttachInfo.mScalingRequired) {
                dirty.inset(-1, -1);
        invalidateRectOnScreen(dirty);
        return null;

又会调用ViewRootImpl中的invalidate方法

void invalidate() {
        mDirty.set(0, 0, mWidth, mHeight);
        if (!mWillDrawSoon) {
            scheduleTraversals();

这里调用了scheduleTraversals重新开始了View的绘制,我们知道View的绘制是从ViewRootImpl的performTraversals方法开始的。我们看看scheduleTraversals是不是触发了performTraversals。

 void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();

在scheduleTraversals方法中我们发现了一个mTraversalRunnable对象,这个对象就是我们要观察的重点

  final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

我们发现这个对象就是一个Runnable对象,我们在scheduleTraversals方法中传入mTraversalRunnable 就会执行run方法,其中又调用了doTraversal这个方法

  void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            performTraversals();
            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;

最后我们发现在doTraversal方法中调用了performTraversals开始了View的重新绘制,这就是invalidate的整个过程。

requestLayout的原理

 public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
            mAttachInfo.mViewRequestingLayout = this;
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;

其中会调用ViewParent的requestLayout方法,同样,我们去看ViewRootImpl中的requestLayout方法。

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();

这里调用了scheduleTraversals,后面的步骤就和上面invalidate时一样了。相对来说,requestLayout的流程还是比较简单的。

既然两种方式都可以完成View的重绘,那么有什么区别呢?
使用invalidate重绘当前视图是不会再次执行measure和layout流程的。因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。
如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了

前言前几篇文章中,笔者对View的三大工作流程进行了详细分析,而这篇文章则详细讲述与三大工作流程密切相关的两个方法,分别是requestLayoutinvalidate,如果对Viwe的三个工作流程不熟悉的读者,可以先看看前几篇文章,以便能更容易理解这篇文章的内容。 前言: 本文是我读《Android内核剖析》第13章----View工作原理总结而成的,在此膜拜下作者。同时真挚地向渴望了解 Android 框架层的网友,推荐这本书,希望你们能够在Android开发里学到更多的知识 。 整个View树的绘图流程是在ViewRoot.java类的perfor... Android提供了Invalidate方法實現界面刷新,但是Invalidate不能直接在線程中調用,因為他是違背了單線程模型:androidUI操作並不是線程安全的,並且這些操作必須在UI線程中調用。invalidate()是用來刷新View的,必須是在UI線程中進行工作。在修改某個view的顯示時,調用invalidate()才能看到重新繪製的界面。invalidate()的調用是把之前的舊... Invalidate() 是 Android 中 View 的方法,通常我们使用它来完成UI的刷新, 如果这个 View 可见那么 onDraw() 方法将在未来某个时间点被调用。 invalidate() 会触发那些 view 的重绘invalidate() 绘制流程是如何实现的? 我们带着问题来从源码开始分析: 一、View 与 ViewGroup 的层级 在 Android. 这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Ma... invalidate()含义 invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。 对于屏幕刷新有以下集中情况可以考虑: 1.不使用多线程和双缓冲 这种情况最简单了,一般只是希望在View发生改变时对UI进行重绘。你只需在Activity中显式地调用View对象中的invalidate()方法即可。系统会自动调用 View的 这两个方法很多人 搞不太清楚,这里小结一下:View的流程图 对于标题提及的两个方法 调用invalidate()或者requestLayout()会触发哪些方法,一图道破天机。源代码现在来看看具体的代码: android.view.ViewRootImpl@Override public void requestLayout() { if (!mHandlingLayoutInLayou 1、invalidate调用后只会触发Draw 过程。 2、requestLayout 会触发Measure、Layout过程,如果尺寸发生改变,则会调用invalidate。 3、当涉及View的尺寸、位置变化时使用requestLayout。 4、当仅仅需要重绘时调用invalidate。 5、如果不确定requestLayout 是 【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】1 背景还记得前面《Android应用setContentView与LayoutInflater加载解析机制源码分析》这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得最终分析结果就是下面的关系:看见没有,如上图中id为content的内容就是整个View树的结构,所 上来先说结论,一言以蔽之: requestLayout方法会导致View的onMeasure、onLayout、onDraw方法被调用;invalidate方法则只会导致View的onDraw方法被调用 具体原理可见文章末尾的这几篇博客,说的已经非常详细,这里只对要点进行记录。 都采用了逐层上报的思想 requestLayout() 子View调用requestLayout方法,会标记当前View及父容器,同时逐层向上提交,直到ViewRootImpl处理该事件,ViewRootImpl会 Invalidate: To farce a view to draw,call invalidate().——摘自View类源码 从上面这句话看出,invalidate方法会执行draw过程,重绘View树。 当View的appearance发生改变,比如状 Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。 Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。   Android程序中可以使用的界面刷新方法有两种,分别是利用Handler和利用postInval... Android自定义View之图像的色彩处理 Android自定义View之图片外形特效——轻松实现圆角和圆形图片 Android自定义View之双缓冲机制和SurfaceView Android自定义View之Window、V... 记录一下前段时间学习的当调用invalidate() 的时候,当前View的onDraw()方法会被调用的原因;通过追踪源码可以发现(我这边看的源码版本是25的): 当我们调用了View的invalidate()时候,invalidate()往下走调用了 invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, ... 从源码看invalidaterequestLayout的区别invalidaterequestLayout经常被用来刷新界面,有的时候2个一起用,TextView的源码里也经常看到2者一起用的情况。什么时候该用哪个呢?为什么有的时候2个要一起用呢?本文所从源码是6.0.1来研究下2者的原理和区别以及如何使用。测试工程InvalidateDemoInvalidatePFLAG_DRAWING_CA 当一个View调用requestLayout的时候,会给当前的View设置一个FORCE_LAYOUT标记。由此向ViewParent请求布局。这样从这个View开始向上一直requestLayout。最终到达ViewRootImpl。ViewParent 就是当前的传输链。【参见职责链设计模式】 ViewRootImpl发现请求了布局。那么就会调用measure方法。