美文网首页安卓开发Android自定义View
Android视图绘制流程之onDraw()

Android视图绘制流程之onDraw()

作者: 天涯的尽头s风沙 | 来源:发表于2019-05-09 09:58 被阅读1次

    measurelayout的过程都结束后,接下来就进入到draw的过程了。

    同样,根据名字你就能够判断出,在这里才真正地开始对视图进行绘制

    ViewRoot中的代码会继续执行并创建出一个Canvas对象,然后调用View的draw()方法来执行具体的绘制工作。

    draw()方法内部的绘制过程总共可以分为六步,其中第二步和第五步在一般情况下很少用到,因此这里我们只分析简化后的绘制过程。代码如下所示:

    public void draw(Canvas canvas) {
        if (ViewDebug.TRACE_HIERARCHY) {
            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
        }
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN;
        // Step 1, draw the background, if needed
        int saveCount;
        if (!dirtyOpaque) {
            final Drawable background = mBGDrawable;
            if (background != null) {
                final int scrollX = mScrollX;
                final int scrollY = mScrollY;
                if (mBackgroundSizeChanged) {
                    background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
                    mBackgroundSizeChanged = false;
                }
                if ((scrollX | scrollY) == 0) {
                    background.draw(canvas);
                } else {
                    canvas.translate(scrollX, scrollY);
                    background.draw(canvas);
                    canvas.translate(-scrollX, -scrollY);
                }
            }
        }
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);
            // Step 4, draw the children
            dispatchDraw(canvas);
            // Step 6, draw decorations (scrollbars)
            onDrawScrollBars(canvas);
            // we're done...
            return;
        }
    }
    

    可以看到,第一步的作用是对视图的背景进行绘制

    • 这里会先得到一个mBGDrawable对象
    • 然后根据layout过程确定的视图位置来设置背景的绘制区域
    • 之后再调用Drawable的draw()方法来完成背景的绘制工作。

    那么这个mBGDrawable对象是从哪里来的呢?其实就是在XML中通过android:background属性设置的图片或颜色。
    当然你也可以在代码中通过setBackgroundColor()、setBackgroundResource()等方法进行赋值。

    接下来,第三步的作用是对视图的内容进行绘制
    这里去调用了一下onDraw()方法

    那么onDraw()方法里又写了什么代码呢?进去一看你会发现,原来又是个空方法啊。其实也可以理解,因为每个视图的内容部分肯定都是各不相同的,这部分的功能交给子类来去实现也是理所当然的。

    接下来第四步的作用是对当前视图的所有子视图进行绘制
    (但如果当前的视图没有子视图,那么也就不需要进行绘制了。)
    因此你会发现View中的dispatchDraw()方法又是一个空方法,而ViewGroup的dispatchDraw()方法中就会有具体的绘制代码。

    以上都执行完后就会进入到第六步,也是最后一步,这一步的作用是对视图的滚动条进行绘制
    那么你可能会奇怪,当前的视图又不一定是ListView或者ScrollView,为什么要绘制滚动条呢?其实不管是Button也好,TextView也好,任何一个视图都是有滚动条的,只是一般情况下我们都没有让它显示出来而已。绘制滚动条的代码逻辑也比较复杂,这里就不再贴出来了,因为我们的重点是第三步过程。

    通过以上流程分析,相信大家已经知道,View是不会帮我们绘制内容部分的,因此需要每个视图根据想要展示的内容来自行绘制。如果你去观察TextView、ImageView等类的源码,你会发现它们都有重写onDraw()这个方法,并且在里面执行了相当不少的绘制逻辑。绘制的方式主要是借助Canvas这个类,它会作为参数传入到onDraw()方法中,供给每个视图使用。Canvas这个类的用法非常丰富,基本可以把它当成一块画布,在上面绘制任意的东西,那么我们就来尝试一下吧。

    这里简单起见,我只是创建一个非常简单的视图,并且用Canvas随便绘制了一点东西,代码如下所示:

    public class MyView extends View {
        private Paint mPaint;
        public MyView(Context context, AttributeSet attrs) {
            super(context, attrs);
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            mPaint.setColor(Color.YELLOW);
            canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
            mPaint.setColor(Color.BLUE);
            mPaint.setTextSize(20);
            String text = "Hello View";
            canvas.drawText(text, 0, getHeight() / 2, mPaint);
        }
    }
    

    可以看到,我们创建了一个自定义的MyView继承自View,并在MyView的构造函数中创建了一个Paint对象。Paint就像是一个画笔一样,配合着Canvas就可以进行绘制了。

    这里我们的绘制逻辑比较简单,在onDraw()方法中先是把画笔设置成黄色,然后调用Canvas的drawRect()方法绘制一个矩形。然后在把画笔设置成蓝色,并调整了一下文字的大小,然后调用drawText()方法绘制了一段文字。

    就这么简单,一个自定义的视图就已经写好了,现在可以在XML中加入这个视图,如下所示:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <com.example.diyview.MyView
            android:layout_width="200dp"
            android:layout_height="100dp" />
    
    </LinearLayout>
    

    将MyView的宽度设置成200dp,高度设置成100dp,然后运行一下程序,结果如下图所示:

    Screenshot_2019-05-09-09-54-04-016_com.example.di.png

    图中显示的内容也正是MyView这个视图的内容部分了。由于我们没给MyView设置背景,因此这里看不出来View自动绘制的背景效果。

    当然了Canvas的用法还有很多很多,这里我不可能把Canvas的所有用法都列举出来,剩下的就要靠大家自行去研究和学习了。

    到此为止,我们把视图绘制流程的第三阶段也分析完了。整个视图的绘制过程就全部结束了,你现在是不是对View的理解更加深刻了呢?

    Home:返回首页

    Previous:onLayout()


    作者:guolin
    来源:CSDN
    原文:https://blog.csdn.net/guolin_blog/article/details/16330267
    版权声明:本文为博主原创文章,转载请附上博文链接!

    相关文章

      网友评论

        本文标题:Android视图绘制流程之onDraw()

        本文链接:https://www.haomeiwen.com/subject/ogeioqtx.html