ViewRoot的draw()分析

    /**
     * ViewRoot中的draw()函数主要处理一些根视图中的特有属性,并且处理完毕后同样要调用View类
     * 中 的draw()进行具体的绘制。
     * Surface按照底层的驱动模式可以分为两种,一种是使用图形加速支持的Surface,俗称显卡,另一
     * 种是使用CPU及内存模拟的Surface。因此,根视图中将针对不同的Surface采用不同的方式从该Surface
     * 中获取一个Canvas对象,并 将 该Canvas对象派发到整个视图中,对于非根视图而言,它并不区分底层
     * 是使用显卡模式,还是使用CPU模式。
     * @param fullRedrawNeeded
     */
    private void draw(boolean fullRedrawNeeded) {
        /**
         * 检 查 Surface是否无效。在正常情况下, Surface都是有效的,除 非WmS发生异常不能为该客
         * 户端分配有效的Surface, isValide()才会返回false。如 果 Surface无效,则终止绘制过程
         */
        Surface surface = mSurface;
        if (surface == null || !surface.isValid()) {
            return;
        }
        /**
         * 执行注册过的Runnable对象。 ViewRoot中使用一个静态列表,可以向该静态列表中添加一些
         * Runnable对象,本步骤则把这些Runnable对象调用post()发送 到Handler队列中,以便下次消息循环时
         * 处理这些Runnable对象。这个变量的名称是sFirstDrawComplete,有些读者可能觉得变量名称有点奇怪,
         * 为什么是Complete呢,明明还没有开始绘制?因为该变量中保存的Runnable对象会在下个消息循环中
         * 执行,而执行前,接下来的绘制过程必须先被执行。
         */
        if (!sFirstDrawComplete) {
            synchronized (sFirstDrawHandlers) {
                sFirstDrawComplete = true;
                for (int i=0; i<sFirstDrawHandlers.size(); i++) {
                    post(sFirstDrawHandlers.get(i));
                }
            }
        }
        /**
         * 调 用 scrollToRectOrFocusO。几乎在所有的情况下,该函数内部都不会执行什么,所以其内部
         * 执行流程忽略。该函数本来的设计目的是对mScmllY变量进行调整,调整的依据是调整到第一个Focus
         * 视图中。
         */
        scrollToRectOrFocus(null, false);
        /**
         * 如果根视图内部包含Scroller对象,则调用该对象的computeScrollOffset()获取新的滚动值,并
         * 赋值给局部变量yoff,关 于Scroller的详细意义将在后面小节中单独介绍。该对象的computeScrollOffset()
         * 的意义是计算是否发生滚动,该函数返回值类型为boolean,比如,当用户在一个ListView上滑动手指
         * 后 ,会在一小段时间内发生滚动,ListView内部有一个Scroller对象,在这个期间调用computeScrollOffsetO
         * 将 返 回true。
         */
        if (mAttachInfo.mViewScrollChanged) {
            mAttachInfo.mViewScrollChanged = false;
            mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
        }
        /**
         * 判 断 该Surface是 否 有SurfaceHolder对象。如果有则意味着该Sb*face是应用程序创建的,因
         * 此所有的绘制操作应该由应用程序自身去负责,于 是 View系统退出绘制。如果不是,才 开 始View绘
         * 制的内部流程。
         */
        int yoff;
        final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
        if (scrolling) {
            yoff = mScroller.getCurrY();
        } else {
            yoff = mScrollY;
        }
        if (mCurScrollY != yoff) {
            mCurScrollY = yoff;
            fullRedrawNeeded = true;
        }
        float appScale = mAttachInfo.mApplicationScale;
        boolean scalingRequired = mAttachInfo.mScalingRequired;

        Rect dirty = mDirty;
        if (mSurfaceHolder != null) {
            // The app owns the surface, we won't draw.
            dirty.setEmpty();
            return;
        }
        /**
         * 如 果 Surface是 由 OpenGL实现的,则开始按照G L 的处理方式进行处理。
         */
        if (mUseGL) {
            if (!dirty.isEmpty()) {
                /**
                 * (1 )以全局变量mGlCanvas作 为 canvas的值,并 调 用canvas.save()保 存 该Canvas内部的各种属性
                 * 及状态,因为接下来View树内部在绘制的过程中会修改Canvas对象的相关属性。
                 */
                Canvas canvas = mGlCanvas;
                if (mGL != null && canvas != null) {
                    mGL.glDisable(GL_SCISSOR_TEST);
                    mGL.glClearColor(0, 0, 0, 0);
                    mGL.glClear(GL_COLOR_BUFFER_BIT);
                    mGL.glEnable(GL_SCISSOR_TEST);

                    mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
                    mAttachInfo.mIgnoreDirtyState = true;
                    mView.mPrivateFlags |= View.DRAWN;
                    /**
                     * (2 )如果根视图内部包含Translator对象,则需要先经过Translator对象对该canvas对象进行一定
                     * 的调整。 Translator的作用主要是根据设备的硬件参数对Canvas的相关绘制属性进行一定的调整,该函
                     * 数的内部一般由驱动设计者支持实现。调整的过程由三个函数调用组成,
                     */
                    int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                    try {
                        canvas.translate(0, -yoff);
                        if (mTranslator != null) {
                            mTranslator.translateCanvas(canvas);
                        }
                        /**
                         * 调 用canvas的 setScreenDensity()设置屏幕密度。这里需要区分不同语境中density的含义的不同。
                         * 首先,定义屏幕密度(density) 概念的作用是为了让不同分辨率的屏幕显示的视图能够看起来大小
                         * 相同。在传统的编程方式中,如果用像素指定视图的大小,对于分辨率高的显示器而言,像素密度高,
                         * 所以相同像素的实际尺寸会变小,于是引入density的概念。 Android中 160dpi的屏幕的density值定义
                         * 为 1,如果屏幕分辨率增加,比如240dpi,那 么 density的值也就越高,为 240/160=1.5,而分辨率低的
                         * 屏幕,比 如 120dpi,其密度就低,为 120/160=0.75。有了 density后,应用程序可以设置视图的大小单
                         * 位 为 dip,即密度无关像素(density independent pixel),从而在绘制视图时, View系统会根据不同的屏
                         * 幕分辨率将其换算成不同的像素。
                         * 而本步设置的screenDensity,却是指屏幕的分辨率值。当 参 数scalingRequired为 true时,该值为
                         * DisplayMetric.DENSITY_DEVICE,该常量是在系统启动时调用getProp()函数获取的设备参数,比如240、
                         * 160、 120等;如 果 scalingRequired为 false,那 么 screenDensity将被赋值为0, 0 是一个特殊值,并不是
                         * 说屏幕分辨率为0 , 而是指视图绘制没有指定具体的分辨率,从而在绘制时一个dpi将对应一个真实的
                         * 物理像素。
                         */
                        canvas.setScreenDensity(scalingRequired
                                ? DisplayMetrics.DENSITY_DEVICE : 0);
                        /**
                         *(4) 调 用 mView.draw(canvas)。该步骤才真正启动视图树的绘制过程,注意这里是将canvas作为
                         *参数,这也就是为什么应用程序中不能保存这个Canvas的原因,因为它是一个临时变量。 mView.draw()
                         * 实际调用的是View类 的 dmw()函数,关于其内部流程将在后面小节中介绍。
                         */
                        mView.draw(canvas);
                        if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
                            mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
                        }
                    } finally {
                        /**
                         * ( 5 ) 完成视图树的绘制后,绘制工作就算结束了,因为调用Canvas的 restoreToCount()将 Canvas
                         * 的内部状态恢复到绘制之前,该步骤与前面的canvas.saveO函数是对称调用的。
                         */
                        canvas.restoreToCount(saveCount);
                    }

                    mAttachInfo.mIgnoreDirtyState = false;

                    mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
                    checkEglErrors();
                    /**
                     * 如 果 是 Debug模式,并且模式中要求显示FPS,即CPU的使用率,则调用一个native函数
                     * nativeShowFPSO给屏幕上方绘制一个条状的统计图。
                     */

                    if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
                        int now = (int)SystemClock.elapsedRealtime();
                        if (sDrawTime != 0) {
                            nativeShowFPS(canvas, now - sDrawTime);
                        }
                        sDrawTime = now;
                    }
                }
            }
            /**
             * 最后,如果屏幕正在滚动,则需要再次发起一个重绘命令scheduleTravasals(),以便接着绘制,
             * 直到滚动结束,滚动的标志scrolling来 源 于Scroller对 象 的computeScrollOffset()函数返回值。
             */
            if (scrolling) {
                mFullRedrawNeeded = true;
                scheduleTraversals();
            }
            return;
        }
        /**
         * 如 果 Surface不 是 OpenGL实现的,则开始按照非G L 的处理方式进行处理。该步骤内部与上
         * 一步基本上是相同的,唯一的区别在于如何获得Canvas对象。 G L方式中,内部使用mGlCanvas全局
         * 变 量 保 存canvas对象,该变量是在G L 的初始化时进行赋值的;而 非 G L 方式中, Canvas对象需要调
         * 用 surface对 象 的lockCanvas()获取,其他过程完全相同,此处不再赘述。
         * 至此, ViewRoot中 的 draw()函数就执行完毕,一次绘制过程也就结束了,下一次的绘制将在下一
         * 个消息循环中执行。
         */
        if (fullRedrawNeeded) {
            mAttachInfo.mIgnoreDirtyState = true;
            dirty.union(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
        }

        if (DEBUG_ORIENTATION || DEBUG_DRAW) {
            Log.v(TAG, "Draw " + mView + "/"
                    + mWindowAttributes.getTitle()
                    + ": dirty={" + dirty.left + "," + dirty.top
                    + "," + dirty.right + "," + dirty.bottom + "} surface="
                    + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
                    appScale + ", width=" + mWidth + ", height=" + mHeight);
        }

        if (!dirty.isEmpty() || mIsAnimating) {
            Canvas canvas;
            try {
                int left = dirty.left;
                int top = dirty.top;
                int right = dirty.right;
                int bottom = dirty.bottom;
                canvas = surface.lockCanvas(dirty);

                if (left != dirty.left || top != dirty.top || right != dirty.right ||
                        bottom != dirty.bottom) {
                    mAttachInfo.mIgnoreDirtyState = true;
                }

                // TODO: Do this in native
                canvas.setDensity(mDensity);
            } catch (Surface.OutOfResourcesException e) {
                Log.e(TAG, "OutOfResourcesException locking surface", e);
                // TODO: we should ask the window manager to do something!
                // for now we just do nothing
                return;
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "IllegalArgumentException locking surface", e);
                // TODO: we should ask the window manager to do something!
                // for now we just do nothing
                return;
            }

            try {
                if (!dirty.isEmpty() || mIsAnimating) {
                    long startTime = 0L;

                    if (DEBUG_ORIENTATION || DEBUG_DRAW) {
                        Log.v(TAG, "Surface " + surface + " drawing to bitmap w="
                                + canvas.getWidth() + ", h=" + canvas.getHeight());
                        //canvas.drawARGB(255, 255, 0, 0);
                    }

                    if (Config.DEBUG && ViewDebug.profileDrawing) {
                        startTime = SystemClock.elapsedRealtime();
                    }

                    // If this bitmap's format includes an alpha channel, we
                    // need to clear it before drawing so that the child will
                    // properly re-composite its drawing on a transparent
                    // background. This automatically respects the clip/dirty region
                    // or
                    // If we are applying an offset, we need to clear the area
                    // where the offset doesn't appear to avoid having garbage
                    // left in the blank areas.
                    if (!canvas.isOpaque() || yoff != 0) {
                        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
                    }

                    dirty.setEmpty();
                    mIsAnimating = false;
                    mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
                    mView.mPrivateFlags |= View.DRAWN;

                    if (DEBUG_DRAW) {
                        Context cxt = mView.getContext();
                        Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
                                ", metrics=" + cxt.getResources().getDisplayMetrics() +
                                ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
                    }
                    int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                    try {
                        canvas.translate(0, -yoff);
                        if (mTranslator != null) {
                            mTranslator.translateCanvas(canvas);
                        }
                        canvas.setScreenDensity(scalingRequired
                                ? DisplayMetrics.DENSITY_DEVICE : 0);
                        mView.draw(canvas);
                    } finally {
                        mAttachInfo.mIgnoreDirtyState = false;
                        canvas.restoreToCount(saveCount);
                    }

                    if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
                        mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
                    }

                    if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
                        int now = (int)SystemClock.elapsedRealtime();
                        if (sDrawTime != 0) {
                            nativeShowFPS(canvas, now - sDrawTime);
                        }
                        sDrawTime = now;
                    }

                    if (Config.DEBUG && ViewDebug.profileDrawing) {
                        EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
                    }
                }

            } finally {
                surface.unlockCanvasAndPost(canvas);
            }
        }

        if (LOCAL_LOGV) {
            Log.v(TAG, "Surface " + surface + " unlockCanvasAndPost");
        }

        if (scrolling) {
            mFullRedrawNeeded = true;
            scheduleTraversals();
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值