Android群侠传第三章阅读笔记(二)

来源:互联网 发布:淘宝时尚女装广告语 编辑:程序博客网 时间:2024/04/30 08:19

上次写到了Android控件架构,这次写一下View相关的一些知识

1. View测量

现实中我们画图形需要知道图形大小以及图形所在的位置,Android中的View绘制也是一样的,在绘制View之前,必须要对View进行大小的测量才能画出View来。这个过程在onMeasure()方法中进行。Android系统给我们提供了一个设计短小精悍却功能强大的类——MeasureSpec类,通过这个类来帮助我们进行View的测量,MeasureSpec是一个32位的int值,其中高2位为测量的模式,低位的30位为测量大小,在计算中使用位运算的原因是为了提高并优化效率。
View的测量模式分为三种。

  • EXACTLY
    即精确值模式,当我们将控件的属性layout_width或layout_height指定为具体数值时候,系统会采用EXACTLY模式。

  • AT_MOST
    即最大值模式,当我们将控件的属性layout?_width或layout_height指定为warp_content时,控件大小一般随着控件的子控件或者控件的内容变化而变化,此时控件的尺寸只要不超过父控件的最大尺寸都是允许的。

  • UNSPECIFIED
    这个属性比较奇怪——他不是指定其大小测量模式,View想多大就多大,通常情况下在绘制自定义View时才会使用。

View类onMeasure()方法一般默认只支持EXACTLY模式,所以自定义控件的时候不重写onMeasure()方法的话,就只能使用EXACTLY模式。控件只能响应指定的具体宽高或者match_parent属性。如果想要支持warp_parent属性,就需要重写onMeasure()方法来指定wrap_parent时的大小。
通过MeasureSpec这个类,我们就可以获取View的测量模式和View想要绘制的大小,有了这些数据信息我们就可以控制View最后显示出来的大小了。
onMeasureSpec方法如下:
这里写图片描述
通过在IDE中按住Ctrl键查看super.onMeasure()方法可以看到系统最后调用setMeasuredDimension(int measuredWidth, int measuredHeight)方法将最后测量的宽高设置进去。
这里写图片描述
所以在重写onMeasure方法后,最终要做的工作就是把测量好的宽高作为参数设置给setMeasuredDimension方法。
在onMeasure()方法中,我们调取自定义的measureWidth()方法和measureHeight()方法,分别对宽高进行测量,参数则是MeasureSpec对象,MeasureSpec对象中包含测量模式和测量值大小。
代码如下:
这里写图片描述

这里写图片描述
基本上我都写了注释就不再写一遍了。下面是效果图:
wrap_content(默认200px的情况,dp的话自己转换一下)
这里写图片描述
400dp
这里写图片描述
macth_content
这里写图片描述
我试了一下padding设置200dp,layout_width跟layout_height设置wrap_content。效果还是100px所以真正做的时候wrap最小值设定那边就不要那样写了。

2. View绘制(因为看的有点晕,后面的基本上就照搬书上的内容了)
当View测量好的时候就开始进行View的绘制了,我们通过地重写onDraw()方法,并在Canvas对象上来绘制所需要的图形。
要想在Android的界面中绘制相应的图像,就必须在Canvas上进行绘制。Canvas就像是一个画板,使用Paint就可以在上面作画。通常需要通过继承View并重写它的onDraw()方法来完成绘图。
那什么是Canvas呢?一般情况下,可以使用重写View类中的onDraw()方法来绘图,onDraw()中有一个参数,就是Canvas canvas对象。使用这个Canvas对象就可以进行绘图了,而在其他地方,通常需要使用代码创建一个Canvas对象。

Canvas canvas=new Canvas(bitmap);

当创建一个Canvas对象时,为什么要传进去一个bitmap对象呢?如果不传入一个bitmap对象,IDE编译虽然不会报错,但是一般我们不会这样做。这是因为传进去的bitmap与通过这个bitmap创建的Canvas画布是紧紧联系在一起,这个过程我们称之为装饰画布。这个bitmap用来存储所有的Canvas上的像素信息。所以当你通过这种方式创建了Canvas对象后,后面调用所有的Canvas.drawXXX方法都发生在这个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已经发生改变。其实并没有将图形直接绘制在onDraw()方法指定的那块画布上,而通过改变bitmap,然后让View重绘,从而显示改变之后的bitmap。**这一过程对初学者来说可能非常难以理解,但是却非常重要,这对后续进行深入地学习和提升绘图技巧非常有帮助。**PS:作者说这个过程是重点,我把他的提示标注一下,过程是绘制的过程我就不标了。

3. 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相同。

4. ViewGroup绘制
ViewGroup通常不需要绘制,因为它本身就没有需要绘制的东西,如果不是指定了ViewGroup的背景色,那么ViewGroup会使用dispacthDraw()方法来绘制其子View,其过程同样是通过遍历所有子View,并调用子View的绘制方法来完成绘制工作的。

0 0