View绘制流程—自定义View相关

一、前期基础知识储备

了解View的绘制流程很大程度上可以帮助我们自定义View的实现,那么本篇文章就将从实现自定义View开始,了解自定义View的常见步骤,然后抓取自定义View的关键步骤,然后在View的绘制流程中重点关注其实现方式。与常规的View绘制方式的分析有所不同(笔者还未达到那个水平,源码暂时扣不动,所以换了一个角度看待这个问题,有取巧嫌疑)

(1)Android中自定义View的步骤

①自定义View属性;

②在View的构造方法中获得自定义的属性;

重写onMeasure() --> 并不是必须的,大部分的时候还需要覆写

重写onDraw()

(2)View在Activity中显示经历的步骤

View在Activity中显示出来,要经历测量、布局和绘制三个步骤,分别对应三个动作:measure、layout和draw。

测量:onMeasure()决定View的大小

布局:onLayout()决定View在ViewGroup中的位置

绘制:onDraw()决定以何种方式绘制这个View。

所以通过自定义View步骤和View显示步骤的分析,可以知道onMeasure()方法和onDraw()方法是在View绘制流程中需要重点关注的。

二、View的measure过程

1)为什么自定义View要使用onMeasure()方法?

分析:Android系统在绘制View前,也必须对View进行测量,即告诉系统该画一个多大的View,这个过程在onMeasure()方法中进行。

Android系统给我们提供了一个短小精悍的类——MeasureSpec类,通过它来帮助我们测量View。MeasureSpec是一个32位的int值,其中高2位为测量模式,低30位为测量的大小,在计算中使用位运算是为了提高并优化效率。测量模式有三种:

UNSPECIFIED

父控件不对你有任何限制,你想要多大给你多大,通常在绘制自定义View时使用。

EXACTLY—View默认

父控件已经知道你所需的精确大小,要么为指定值要么为match_parent属性。

AT_MOST—自定义View使用到onMeasure()方法的根本原因

即最大值模式,当控件的layout_width属性或者layout_height属性指定为wrap_content时,控件大小一般随着控件的子控件或内容的变化而变化,但是子控件的大小不能大于父控件给你指定的size,但具体是多少,得看你自己的实现。

解答:View类默认的onMeasure()方法只支持EXACTLY模式,所以如果在自定义控件的时候不重写onMeasure()方法的话,就只能用EXACTLY模式,控件可以响应你指定的具体宽高值。而如果要让自定义View支持wrap_content属性,那么就必须重写onMeasure()方法来指定wrap_content时的大小。

(2)如何进行View的测量measure

①重写onMeasure()方法

@Override

Protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){

        setMeasuredDimension(

            measureWidth(widthMeasureSpec);//自定义的measureWidth()

            measureHeight(heightMeasureSpec);//自定义的measureHeight()

        )//利用自定义的两个方法对宽高重新定义

}//传入的参数即为开篇时提到的强大的类——MeasureSpec类

②实现自定义的measureWidth()measureHeight()和方法

private int measureWidth(int measureSpec){

    int result = 0;

    int specMode = MeasureSpec.getMode(measureSpec);//取出测量模式

    int specSize = MeasureSpec.getSize(measureSpec);//取出测量大小

    if(specMode == MeasureSpec.EXACTLY){

        result = specSize;//EXACTLT时,直接使用指定值或者match_parent

    }else{

        result = 200;//人为给出指定大小

        If(specMode ==MeasureSpec.AT_MOST){

        result = Math.min(result,specSize);//取小

    }

}

return result;

};//这段代码可以作为自定义View时的onMeasure()方法的模板代码

三、View的draw过程

当测量好一个View之后,我们就可以简单地重写onDraw() 方法,并在Canvas对象上来绘制所需要的图形。首先我们来了解一下利用系统2D绘图API所必须要使用到的Canvas对象。

要想在Android的界面中绘制相应的图像,就必须在Canvas上进行绘制。Canvas就像是一个画板,使用Paint就可以在上面作画了。通常需要通过继承View并重写它的onDraw()方法来完成绘图。

onDraw()方法中有一个参数就是Canvas canvas对象。

Canvas canvas = new Canvas(bitmap);//创建canvas对象时,传入bitmap

Bitmap与通过这个bitmap创建的Canvas画布是紧紧联系在一起的,这个过程我们称之为装载画布。这个bitmap用来存储所有绘制在Canvas上的像素信息。所以当你通过这种方式创建了Canvas对象后,后面调用的所有的Canvas.drawXXX方法都发生在这个bitmap上。例如,首先在onDraw()方法中绘制两个bitmap:

canvas.drawBitmap(bitmap1,0,0,null);

canvas.drawBitmap(bitmap2,0,0,null);

而对于bitmap2,我们将它装载到另一个Canvas对象中,代码如下,

Canvas mCanvas = new Canvas(bitmap2);

在其他地方使用Canvas对象的绘图方法在装载bitmap2的Canvas对象上进行绘图,代码:mCanvas.drawXXX

通过mCanvas将绘制效果作用在bitmap2上,再刷新View时会发现通过onDraw()方法画出来的bitmap2已经发生了改变,这就是因为bitmap2承载了在MCanvas上所进行的绘图操作。

注:Canvas—画板;bitmap—画布;paint—画笔

四、ViewGroup的绘制过程

ViewGroup容器控件会去管理其内的子View,其中一个管理项目就是负责子View的显示大小。当ViewGroup的大小为wrap_content时ViewGroup就需要对View进行遍历,以便获得所有View的大小,从而来决定自己的大小。而在其他两种模式下则会通过其指定的值来进行设置。

ViewGroup在测量是通过遍历所有子View,从而调用子View的Measure方法来获得每个子View的测量结果,前面第二节中对View的测量,就是在这里进行的。当子View测量完毕后,就需要将子View放到合适的位置,这个过程就是View的Layout过程。ViewGroup在执行Layout过程时,同样是使用遍历来调用子View的Layout方法,并指定具体显示的位置,从而来决定其布局位置。

在自定义ViewGroup时,通常会去重写onLayout()方法来控制其子View显示位置的逻辑。同样,如果需要支持wrap_content属性,那么它还必须重写onMeasure()方法,这点与View是相通的。

注:ViewGroup一般不需要绘制,因为它本身没有需要绘制的东西。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值