概述

Android中View框架的工作机制中,主要有三个过程:

1、View树的测量(measure)Android View框架的measure机制

2、View树的布局(layout)Android View框架的layout机制

3、View树的绘制(draw)Android View框架的draw机制

View框架的工作流程为:测量每个View大小(measure)-->把每个View放置到相应的位置(layout)-->绘制每个View(draw)。

本文主要讲述三大流程中的draw过程。不清楚measure过程的,可以参考这篇文章 Android View框架的measure机制

不清楚layout过程的,可以参考这篇文章Android View框架的layout机制

这篇文章不讲述graphic模块具体绘制API的使用,只是描述View框架的绘制过程。

带着问题来思考整个draw过程。

1、系统为什么要有draw过程?

View框架在经过了measure过程和layout过程之后,就已经确定了每一个View的尺寸和位置。那么接下来,也是一个重要的过程,就是draw过程,draw过程是用来绘制View的过程,它的作用就是使用graphic框架提供的各种绘制功能,绘制出当前View想要的样子。

2、draw过程都干了点什么事?

View框架中,draw过程主要是绘制View的外观。ViewGroup除了负责绘制自己之外,还需要负责绘制所有的子View。而不含子View的View对象,就负责绘制自己就可以了。

draw过程的主要流程如下:

1、绘制 backgroud(drawBackground)
2、如果需要的话,保存canvas的layer,来准备fading(不是必要的步骤)
3、绘制view的content(onDraw方法)
4、绘制children(dispatchDraw方法)
5、如果需要的话,绘制fading edges,然后还原layer(不是必要的步骤)
6、绘制装饰器、比如scrollBar(onDrawForeground)

源代码分析

在View的源代码中,提取到了下面一些关于layout过程的信息。

我们知道,整棵View树的根节点是DecorView,它是一个FrameLayout,所以它是一个ViewGroup,所以整棵View树的测量是从一个ViewGroup对象的draw方法开始的。

View:

1、draw

/**

绘制一个View以及他的子View。最好不要覆写该方法,应该覆写onDraw方法来绘制自己。

*/

public void draw(Canvas canvas);

源代码:

这里不给出,有兴趣的读者,可以自行查阅SDK。

伪代码

public void draw(Canvas canvas) {1、绘制 backgroud(drawBackground)  ;2、如果需要的话,保存canvas的layer,来准备fading ;3、绘制view的content(onDraw方法);4、绘制children(dispatchDraw方法);5、如果需要的话,绘制fading edges,然后还原layer ;6、绘制装饰器、比如scrollBar(onDrawForeground);}


2、onDraw

/**

绘制一个View的外观。View的默认实现是空实现,所以这里没有源码给出。

*/

protected void onDraw(Canvas canvas);

ViewGroup:

1、dispatchDraw

/**绘制子View,View类是空实现,ViewGroup类中有实现 */

protected void dispatchDraw(Canvas canvas);

源代码:

这里不再给出,有兴趣的读者自行查阅SDK。

伪代码:

protected void dispatchDraw(Canvas canvas) {    if (需要绘制布局动画) {for (遍历子View) {    绑定布局动画;}启动动画控制,通知动画开始;    }    for (遍历子View) {child.draw();    }}


动手操作

下面我们自己写一个自定义的ViewGroup,让它内部的每一个子View都垂直排布,并且让每一个子View的左边界都距离上一个子View的左边界一定的距离。然后在整个布局内部绘制一个圆形。大概看起来如下图所示:

Android View框架的draw机制_第1张图片

实际运行效果如下:

Android View框架的draw机制_第2张图片

代码如下:

public class VerticalOffsetLayout extends ViewGroup {    private static final int OFFSET = 100;    private Paint mPaint;    public VerticalOffsetLayout(Context context) {        super(context);        init(context, null, 0);    }    public VerticalOffsetLayout(Context context, AttributeSet attrs) {        super(context, attrs);        init(context, attrs, 0);    }    public VerticalOffsetLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context, attrs, defStyleAttr);    }    private void init(Context context, AttributeSet attrs, int defStyleAttr) {        mPaint = new Paint(Color.BLUE);        mPaint.setAntiAlias(true);        mPaint.setAlpha(125);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int width = 0;        int height = 0;        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            ViewGroup.LayoutParams lp = child.getLayoutParams();            int childWidthSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);            int childHeightSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);            child.measure(childWidthSpec, childHeightSpec);        }        switch (widthMode) {            case MeasureSpec.EXACTLY:                width = widthSize;                break;            case MeasureSpec.AT_MOST:            case MeasureSpec.UNSPECIFIED:                for (int i = 0; i < childCount; i++) {                    View child = getChildAt(i);                    int widthAddOffset = i * OFFSET + child.getMeasuredWidth();                    width = Math.max(width, widthAddOffset);                }                break;            default:                break;        }        switch (heightMode) {            case MeasureSpec.EXACTLY:                height = heightSize;                break;            case MeasureSpec.AT_MOST:            case MeasureSpec.UNSPECIFIED:                for (int i = 0; i < childCount; i++) {                    View child = getChildAt(i);                    height = height + child.getMeasuredHeight();                }                break;            default:                break;        }        setMeasuredDimension(width, height);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int left = 0;        int right = 0;        int top = 0;        int bottom = 0;        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            left = i * OFFSET;            right = left + child.getMeasuredWidth();            bottom = top + child.getMeasuredHeight();            child.layout(left, top, right, bottom);            top += child.getMeasuredHeight();        }    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        int x = getWidth()/2;        int y = getHeight()/2;        canvas.drawCircle(x, y, Math.min(x, y), mPaint);    }}


更多相关文章

  1. Android ORM应用开发框架KJFrameForAndroid使用详解
  2. Android知识梳理:消息机制之Handler
  3. Android 接口回调机制
  4. eclipse android jni 和 ndk以及调用过程--tread
  5. 浅析:Android 嵌套滑动机制(NestedScrolling)
  6. Android 快速开发框架

随机推荐

  1. 错误总结1,动态加载部分不能显示的原因
  2. android tv基础之焦点(二)
  3. Android(安卓)捕获运行时异常详解
  4. JNI开发第二步:20130726 NDK_JNI使用
  5. Android(安卓)Native层Binder.transact()
  6. Android(安卓)LsitView的实现
  7. 栈处理问题
  8. android back和home键的捕获
  9. Android——拍照、剪切、得到图片/从相册
  10. android2.1短信应用