Android View框架的draw机制

概述

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的左边界一定的距离。然后在整个布局内部绘制一个圆形。大概看起来如下图所示:

实际运行效果如下:

代码如下:

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);
    }
}
时间: 2024-10-12 04:54:21

Android View框架的draw机制的相关文章

Android View框架的measure机制

概述 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).          本文

Android View框架的layout机制

概述 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).          本文主要讲

Android View框架总结(八)ViewGroup事件分发机制

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52298780 上篇分析了View的事件分发流程,留了一个问题:如果上面的EventButton继承TextView的话,按下抬起,会有一个现象,我可以告诉大家现象:就是只有dispatchTouchEvent ACTION_DOWN,onTouch ACTION_DOWN,onTouchEvent ACTION_DOWN这三个,你移动,或者抬起,是没有MOVE,或者

Android View的事件分发机制探索

概述 Android事件传递机制也是Android系统中比较重要的一块,事件类型有很多种,这里主要讨论TouchEvent的事件在framework层的传递处理机制.因为对于App开发人员来说,理解framework层的事件传递机制,就差不多了. 带着问题来思考整个事件分发过程. 1.为什么要有事件分发过程? 当Android设备的屏幕,接收到触摸的动作时,屏幕驱动把压力信号(包括压力大小,压力位置等)传递给系统底层,然后操作系统经过一系列的处理,然后把触摸事件一层一层的向上传递,最终事件会被准

Android View 触摸事件传递机制

PS:以现在的眼光看以前写的博客感觉写的很烂,或许或一段时间再看现在的博客会有同样的感觉.所以每时每刻都去学习,去发现和理解新的东西. 引言 由于之前写的一篇关于Android事件传递顺序的博客质量太差,可能是理解不到位的原因,故最近又花了许多时间再次去看Android源码,看完之后有了新的理解,所以打算重新整理这篇博客.理解Android触摸事件传递机制有助于日后的开发以及自定义一些手势效果等.注意:这篇博客是基于Android2.0源码来分析的,不管老版本还是新版本的Android,其内部触

Android View框架总结(七)View事件分发机制

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52282833 View布局告一段落,从本篇开始View事件相关分析,今天分析的是View的事件分发机制(PS:本篇文章中源码均是android 6.0,请知晓) View 事件的分发机制 dispatchTouchEvent onInterceptTouchEvent onTouchEvent 案例 事件通常重要的有如下三种: MotionEvent.ACTION_

Android View的事件分发机制

准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup.如RelativeLayout.LinearLayout.作为一个开发人员,我们会思考.当点击一个button,Android系统是如何确定我点的就是button而不是TextView的?然后还正确的响应了button的点击事件. 内部经过了一系列什么过程呢? 先铺垫一些知识能更加清晰的理解事件分

Android View框架总结(二)View焦点

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52263256 前言:View框架写到第六篇,发现前面第二篇竟然没有,然后事情是在微信公众号发了,忘记在博客上更新,所以关注微信公众号的应该都看过了,趁今天有时间遂补上.(PS:本篇文章中源码均是android 6.0,请知晓) 本来之前说view下篇是写onMeasure,onLayou,onDraw相关的,笔者做盒子开发,遥控器按键,碰到的都是焦点控制相关.所以先

Android view 的事件分发机制

1 事件的传递顺序是 Activity -> Window -> 顶层View touch 事件产生后,最先由 activity 的 dispatchTouchEvent 处理 /** * Called to process touch screen events. You can override this to * intercept all touch screen events before they are dispatched to the * window. Be sure to