自定义 View —— invalidate() 源码粗略浏览

目录

invalidate

checkThread


大家都知道,调用 invalidate 会不断的调用 onDraw 方法 重新绘制,今天,看一下 invalidate 里面大概干了什么?

源码版本:android-27

依然是不停的往下找。。。。

invalidate

    public void invalidate() {
        invalidate(true);
    }

    public void invalidate(boolean invalidateCache) {
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }

    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
            ......
            // 判断是否跳过重绘
            if (skipInvalidate()) {
                return;
            }
            ......
            // 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);
                // 将要重绘的区域传递给父 View,也就是 ViewGroup 的 invalidateChild 方法
                p.invalidateChild(this, damage);
            }
            ......
    }

然后我们看一下  ViewGroup 的 invalidateChild 方法

    public final void invalidateChild(View child, final Rect dirty) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null && attachInfo.mHardwareAccelerated) {
            // HW accelerated fast path
            // 开启了硬件加速,开启后,可以提高重绘的效率
            onDescendantInvalidated(child, child);
            return;
        }
        ......
        do {
            View view = null;
            if (parent instanceof View) {
                view = (View) parent;
            }
        // 调用了 parent 的 invalidateChildInParent 方法,也就是 ViewGroup 的             
        // invalidateChildInParent 方法
        parent = parent.invalidateChildInParent(location, dirty);
        if (view != null) {
               // Account for transform on current parent
               Matrix m = view.getMatrix();
               if (!m.isIdentity()) {
                   RectF boundingRect = attachInfo.mTmpTransformRect;
                   boundingRect.set(dirty);
                   m.mapRect(boundingRect);
                   dirty.set((int) Math.floor(boundingRect.left),
                           (int) Math.floor(boundingRect.top),
                           (int) Math.ceil(boundingRect.right),
                           (int) Math.ceil(boundingRect.bottom));
               }
           }
       } while (parent != null);
      }  
    }


    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
            // either DRAWN, or DRAWING_CACHE_VALID
            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
                    != FLAG_OPTIMIZE_INVALIDATE) {
                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
                        location[CHILD_TOP_INDEX] - mScrollY);
                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
                }

                final int left = mLeft;
                final int top = mTop;

                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
                    if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
                        dirty.setEmpty();
                    }
                }

                location[CHILD_LEFT_INDEX] = left;
                location[CHILD_TOP_INDEX] = top;
            } else {

                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
                    dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
                } else {
                    // in case the dirty rect extends outside the bounds of this container
                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
                }
                location[CHILD_LEFT_INDEX] = mLeft;
                location[CHILD_TOP_INDEX] = mTop;

                mPrivateFlags &= ~PFLAG_DRAWN;
            }
            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            if (mLayerType != LAYER_TYPE_NONE) {
                mPrivateFlags |= PFLAG_INVALIDATED;
            }

            return mParent;
        }

        return null;
    }

invalidateChild 方法中的 do.....while 循环,会不断的 去执行父 View 的  invalidateChildInParent 方法,最后会找到最顶层的 ViewParent ,也就是 ViewRootImpl,所以最后会在 ViewRootImpl 中执行 invalidateChildInParent 方法:

    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        // 先执行 checkThread
        checkThread();
        ......
        // 最后执行 这个方法
        invalidateRectOnScreen(dirty);
        // 最后返回 null,所以执行完 invalidateChildInParent() 方法后 parent 被赋值为
        // null,退出 do-while 循环
        return null;
    }

checkThread

先看一下 checkThread() 方法,我们在子线程中更新UI,报错的地方,最后会指向 这个地方

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

这个错误,大家很熟悉吧,判断当前线程是否是主线程,不是的话,就抛异常报错。

所以 invalidate()方法必须在主线程中调用!!

再继续看 最后调用的 invalidateRectOnScreen(dirty) 方法

    private void invalidateRectOnScreen(Rect dirty) {
        ......
        if (!mWillDrawSoon && (intersected || mIsAnimating)) {
            // 最后调用 这个方法
            scheduleTraversals();
        }
    }

scheduleTraversals() 方法中,通过  postCallback 最后执行 runnable 中的 doTraversal() 方法

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

    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            // 最终调用 doTraversal 方法
            doTraversal();
        }
    }

doTraversal() 方法中 会调用 performTraversals()方法,然后开始 View 的三大流程

    void doTraversal() {
            ......
            // 进入 performTraversals
            performTraversals();
            ......
    }

    private void performTraversals() {
      ......
      //
      boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
      ......
      // Ask host how big it wants to be
      // 调用performMeasure()方法开始measure流程
      performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
      ......
      // 开始layout流程
       performLayout(lp, mWidth, mHeight);
      .....
      // 开始 draw 流程
       performDraw();
      .....

    }

因为此时没有给 mLayoutRequested 赋值,默认值为false,所以不会调用 performMeasure 和 performLayout 方法,只调用了 performDraw 方法,也就是说调用 invalidate 时,不会执行 measure 和 layout 流程,只执行 draw,之后就会调用DecorView 的 draw 方法,遍历 DecorView 的子 View,逐层完成对子 View 的绘制。

我们继续往下看:

    private void performDraw() {
        ......
        try {
            // 调用 draw 方法
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        ......
    }

    private void draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;
        ......
                // draw 方法中再调用 drawSoftware
                /** 第一找真的不好找到这个方法!!onDraw 的逻辑竟然在这里!! **/
                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
        ......
    }
    
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        // Draw with software renderer.
        final Canvas canvas;
        ......
            // 最后还是通过 Surface 来处理的, 锁定画布
            canvas = mSurface.lockCanvas(dirty);    
        ......
        // 这里的 mView是 最上层的 ViewGroup, 他从最上层 ViewGroup 一直调用到最底层 view,不        
        // 断的从 draw 方法调用 drawBackground -> onDraw-> dispatchDraw -> onDrawForeground
        mView.draw(canvas);
        ......
    }

从这里我们可以看到,每次 invalidate ,都会从最上层 的 ViewGroup 一直到 最低层的 view 都进行绘制,如果嵌套太多的话,真的会消耗性能。

但是,开启硬件加速可以提高重绘的效率

关闭硬件加速:绘制是从 DecorView 开始,对所有 View 都会重新绘制

开启硬件加速:只有在调用 invalidate 方法的 View中(包括它的子View)才会完成重新绘制

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值