我们知道android SDK中的UI控件都是View或ViewGroup的子类(ViewGroup也是View的子类),我们将View细分为单独View和容器View两种,所亦就衍生两个视图基类:View和ViewGroup。通过扩展这两个基类,Android SDK提供了一系列害能强大,设计巧妙的UI控件,但是用户的需求是千变万化的,SDK中不可能提供所有可能用到的UI形式,所亦这就需要允许用户自定义View对象来完成他们所需要的效果。
幸好,Android SDK有支持自定义View对象,它允许你自定义一个类,继承android.view.View对象,然后你只需要调整它里面的一些方法即可,那到底要重写哪些方法呢?如果你对android.view.View本身都不是很熟悉的话,那怎么会自定义view呢,就像是还不会走路,怎么就想学跑步了,所亦我们首先要先了解一些关于View的东西。
了解 android.view.View我们在了解Activity时就有提到,只要有能看到的UI界面,就一定有Activity。这边所说的UI界面就是指View对象,所亦View和Activity离不开关系。当Activity处于Active状态时(Activity获取了当前的焦点),它就发出请求要求绘制Activity中的View对象。系统根据视图的关系层次从根节点出发开始绘制直到所有的叶子节点(因为View是有层次关系的),整个绘制过程中View和ViewGroup的实现形式是不一样的,因为ViewGroup里面会有子视图对象,所亦它需要请求子视图执行绘制,整个视图树执行绘图的顺序是中序遍历,从根节点先画,最后才是子节点。如果是View那么只要确定了它的大小即可执行绘制方法。
绘制ViewGroup时可亦分为两个步骤:measure测量过程和layout布局过程。measure是用来测量ViewGroup所占用的尺寸,它按照中序遍历的方式递归调用子视图的measure方法,然后保存各个子视图对应测量出来的结果。当measure测量完成后就会调用layout过程,layout过程也是使用中序遍历递归调用,它做的工作是确定每个父视图的尺寸,因为在measure阶段已经完成了所有的子视图尺寸,所亦父视图的尺寸也可亦通过它计算出来。
当measure测量执行完成后,你就已经知道每个视图所需的尺寸了,所亦你需要设置值,让getMeasureWidth和getMeasureHeight方法可亦获取测量得到的结果。measure测量的结果必须考虑父视图设置的长度,确保测量完成后得到的结果满足:所有的父视图的尺寸都能装得下它们各自的所有子视图。一个父视图可能会多次调用它的子视图的measure方法,因为视图可亦设置成最大尺寸(fill_parent),它首先会不限制子视图的大小,让子视图能尽量大,但是如果有两个子视图都采用最大尺寸(fill_parent),那么只能采取折中方式。
measure测量过程用两个类来协助确定尺寸:MeasureSpec和LayoutParams。MeasureSpec用来对父视图描述它需要的尺寸或位置,LayoutParams用来描述它需要的宽度和高度,它可定义的尺寸分为3种模型:
确定值:直接确定它的大小FILL_PARENT:填充父视图的空间(最大占用空间)WRAP_CONTENT:能刚好显示出视图内容的大小区域即可(最小占用空间)MeasureSpec用来让父视图通知子视图,子视图所能分配的大小,它分为下面3种模型:
UNSPECIFIED:让父视图直接按照子视图需求的尺寸进行分配,比如一个LinearLayout在measure中就对子视图使用UNSPECIFIED模型,它不限制大小,按照子视图的需求进行分配。EXACTLY:强迫子视图的尺寸必须按照父视图分配的尺寸进行测量,如果子视图的实际尺寸比这个尺寸大则需要缩小,如果子视图的实际尺寸比这个尺寸小则需要扩大。AT_MOST:强迫让子视图使用最大的尺寸,子视图的尺寸不得大于父视图定义的最大尺寸,但是可亦小于这个最大尺寸值(跟EXACTLY不一样的是它可亦小于这个尺寸值)。创建自定义View对象了解了View测量和绘制的原理后,你就可亦通过继承它,重写特定的方法创建一个自定义View对象。一般创建自定义View的用途是:
1. 自定义视图对象,创建一个SDK没提供的布局形式,如Accordion。
2. 将多个View对象组合起来封装成一个新的自定义View对象,如Combox就是由一个下拉列表和一个输入框和输入框右边一个按钮组成的。
3. 改写EditText对象,Android SDK中的记事本例子就使用了扩展EditText的视图,它在每行文字的下面画一条蓝色的线。
4. 创建一个游戏面板,捕捉一些键盘或触摸事件做出一些特定的处理。
如何创建自定义View对象主要有下面几个步骤:
1. 创建一个自定义类,继承android.view.View。
2. 重写父类构造方法,你可亦在构造方法中获取在xml布局文件里设置的属性,你可亦从这里读取一些自定义的属性配置View对象。
3. 重写父类(View)的方法,这边可亦根据需要修改的程度有选择的重写,这些重写的方法都是on开头的方法如:onDraw、onMeasure或onkeydown(这些跟Activity生命周期中的onCreateonPause…一样也算绘制过程的生命周期)。如果你没有重写onDraw默认它不做任何事,所亦你会看到一片空白区域,onMeasure默认会创建一个100 * 100的区域
4. 使用这个对象
一般我们如果没有在xml布局文件中使用一些自定义的配置属性可亦不用重写构造方法,这时候我们只需要重写onMeasure和onDraw方法。onMeasure方法被调用时有两个参数:widthMeasureSpec和heightMeasureSpec这个参数是int型,用来表示宽度和高度的尺寸,在onMeasure中必须根据这两个参数确定View的宽度和高度值,然后调用setMeasureDimension(int widthint height)方法设置视图的显示区域范围,否则将会抛出异常。在onDraw方法中,会有一个调用参数:Canvas,你可亦使用它进行绘制View的操作。
本文转自: http://www.shunyi.net/forum/view/id-31356