ViewGroup类 中 绘 制 子 视 图dispatchDraw()内部流程

  /**
     * dispatchDraw()的作用是绘制父视图中包含的子视图,该函数的本质作用是给不同的子视图分配合
     * 适 的 画 布 (Canvas),至于子视图如何绘制,则又递归到View类 的 draw()函数中。应用程序一般不需要
     * 重 载 dispatchDrawO函数,而只需要在onLayout()中为子视图分配合适的大小, dispatchDraw()将根据前
     * 面分配的大小调整Canvas的内部剪切区,并作为绘制子视图的画布。所有的ViewGroup实例的内部绘
     * 制基本上都是如此,这就是为什么具体的ViewGroup实例不需要重载dispatchDraw()的原因。
     */
    @Override
    protected void dispatchDraw(Canvas canvas) {
        final int count = mChildrenCount;
        final View[] children = mChildren;
        int flags = mGroupFlags;
        /**
         * 判 断 mGroupFlags中是否设置FLAG—RUN—ANIMATION标识,该标识并不是该ViewGroup的
         * “动画标识”,而是 该ViewGroup “布局动画标识”。动画标识指的是一个View自身的动画,而布局动
         * 画只存在于ViewGroup对象中,指的是该ViewGroup在显示内部的子视图时,为内部子视图整体设置
         * 的 动 画 。 典 型 的 例 子 就 是 , 应 用 程 序 可 以 在 X M L 文 件 中 的 LinearLayout标 签 中 设 置
         * android:layoutAnimation属性,从而使 该LinearLayout的子视图在显示时出现逐行显示、随机显示、落
         * 下等不同的动画效果,而这些效果正是在本步骤实现的。关于动画的详细过程见后面小节,本节只分析
         * 没有动画的情况。
         */
        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
            final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;

            for (int i = 0; i < count; i++) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                    final LayoutParams params = child.getLayoutParams();
                    attachLayoutAnimationParameters(child, params, i, count);
                    bindLayoutAnimation(child);
                    if (cache) {
                        child.setDrawingCacheEnabled(true);
                        child.buildDrawingCache(true);
                    }
                }
            }

            final LayoutAnimationController controller = mLayoutAnimationController;
            if (controller.willOverlap()) {
                mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
            }

            controller.start();

            mGroupFlags &= ~FLAG_RUN_ANIMATION;
            mGroupFlags &= ~FLAG_ANIMATION_DONE;

            if (cache) {
                mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
            }

            if (mAnimationListener != null) {
                mAnimationListener.onAnimationStart(controller.getAnimation());
            }
        }
        /**
         * 处理padding属性。该属性是ViewGroup特有的,程序员只能给一个ViewGroup设 置padding,
         * 而不能给一个View设 置padding。如 果ViewGroup包 含padding值 ,则 CLIP_PADDINT—MASK标识将
         * 存在。对 于 View系统而言,当绘制到某个View时, View系统并不区分该View是一个具体的Veiw还
         * 是一个ViewGroup实例,都会在View.draw()函数中调用dispatchDraw(canvas),参 数 Canvas的绘制区原
         * 点坐标是该View内部区域的左上角, Canvas的剪切区仅仅是根据scrool值进行了剪切。由于padding
         * 是 ViewGroup所特有的属性,因此ViewGroup的 dispatchDraw()需要对该属性进行自身的处理。
         * 源码中首先调用canvas.save()保 存 当 前Canvas内部状态,然 后 调 用canvas.clipRect()进行剪切。在
         * 执 行 dispatchDraw()函数前, Canvas的剪切区已经根据scroll值进行了剪切,剪切坐标的原点是View自
         * 身的左上角,所以此处仅仅需要从左边加paddingLeft,从上边加paddingTop,从右边减paddingRight,
         * 从下边减paddingBottom。
         * 执行后,就会根据padding的值缩小剪切区。这里需要注意,缩小的仅仅是剪切区,也就是用户在
         * 屏幕上看到的区域,而 ViewGmup本身的大小没有变化。
         */
        int saveCount = 0;
        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
        if (clipToPadding) {
            saveCount = canvas.save();
            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
                    mScrollX + mRight - mLeft - mPaddingRight,
                    mScrollY + mBottom - mTop - mPaddingBottom);

        }
        /**
         * 清 除 mPrivateFlags的 DRAW_ANIMATION标 识 ,因为接下来就会绘制视图了;同时清除
         * mGroupFlags的 FLAG—INVALIDATED_REQUJRIED标 识 , 因 为 接 来 绘 制 后 就 意 味 着 已 经 满 足
         * "RECURIED” 这个需求了。
         */
        // We will draw our child's animation, let's reset the flag
        mPrivateFlags &= ~DRAW_ANIMATION;
        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;

        boolean more = false;
        final long drawingTime = getDrawingTime();
        /**
         * 使 用 for()循环,针 对 该ViewGroup的子视图逐个调用drawChild()函数。在一般情况下,绘制
         * 子 视 图 的 顺 序 是 按 照 子 视 图 被 添 加 的 顺 序 逐 个 绘 制 , 但 应 用 程 序 可 以 重 载 ViewGmup的
         * getChildDrawingOrder()函数,提供不同的顺序。关 于 drawChild()的内部过程见后面小节
         */
        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
            for (int i = 0; i < count; i++) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        } else {
            for (int i = 0; i < count; i++) {
                final View child = children[getChildDrawingOrder(count, i)];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        }

        // Draw any disappearing views that have animations
        /**
         * 绘 制 mDisappearingChildren列表中的子视图。这个变量需要着重解释一下,当 从 ViewGroup
         * 中 removeView()时,指 定 的View对象会从mChildren变量中移除,因此,当进行消息派发时,被删除
         * 的 View就绝不会获得用户消息。当被删除的View对象包含一个移除动画时,则 该 View会被添加到
         * mDisappearingChildren列表中,从而使得在进行dispatchDraw()时,该 View依然会被绘制到屏幕上,直
         * 到动画结束,在动画期间,用户虽然能够看到该视图,但却无法点击该视图,因为它已经从mChildren
         * 列表中被删除,消息处理时会认为没有该View的存在。
         */
        if (mDisappearingChildren != null) {
            final ArrayList<View> disappearingChildren = mDisappearingChildren;
            final int disappearingCount = disappearingChildren.size() - 1;
            // Go backwards -- we may delete as animations finish
            for (int i = disappearingCount; i >= 0; i--) {
                final View child = disappearingChildren.get(i);
                more |= drawChild(canvas, child, drawingTime);
            }
        }

        if (clipToPadding) {
            canvas.restoreToCount(saveCount);
        }

        // mGroupFlags might have been updated by drawChild()
        flags = mGroupFlags;
        /**
         * 6、 重新检查 mGroupFlags 中是否包含 FLAG_INVALIDATED_REQURIED 标识,因为 drawChild()
         * 调用后,可能需要重绘该ViewGroup,如果需要,贝U调 用 invalidate()发起一个重绘请求。
         */
        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
            invalidate();
        }
        /**
         * 7 、本步骤与第Q 步是对称的,第+ 步中会先处理“布局动画”,而本步骤则处理布局动画是否完
         * 成,如果完成,发 送 一 个 Handler消息。该 消 息 是 一 个Runnable对象,其 作 用 是 回 调ViewGroup中
         * AnimationListener接 口 的onAnimationEnd()函数,通知应用程序布局动画完成了。
         */
        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
                mLayoutAnimationController.isDone() && !more) {
            // We want to erase the drawing cache and notify the listener after the
            // next frame is drawn because one extra invalidate() is caused by
            // drawChild() after the animation is over
            mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
            final Runnable end = new Runnable() {
               public void run() {
                   notifyAnimationListener();
               }
            };
            post(end);
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值