转载地址:http://blog.csdn.net/iefreer/archive/2009/09/26/4598932.aspx
创建自定义组件
Building Custom Components
Android
提供了一个精致而强大的组件化模式来创建你的用户界面,基于基础的布局类:视图
View
和视图组
ViewGroup
。平台包含了多种预定义视图和视图组子类
-
分别称为部件和布局
-
这些可以用来构造你的用户界面。
一部分可用部件包括按钮
Button
,文本视图
TextView
,编辑文本框
EditText
,列表视图
ListView
,组合框
CheckBox
,
单选按钮
RadioButton
,
画廊
Gallery
,
微调器
Spinner
,
以及一些用于特定场合的自动补全文本视图
AutoCompleteTextView
,
图片切换器
ImageSwitcher
,
和文本切换器
TextSwitcher
.
可用布局有线性布局
LinearLayout
,
框架布局
FrameLayout
,
相对布局
RelativeLayout
,
以及其他。更多例子,参见常用布局对象
Common Layout Objects
.
如果这些预定义的部件或布局不能满足你的需求,你可以创建你自己的视图类。如果你只需要在现有的部件或布局上做些调整,你只需要子类化这个部件或布局并重写它的方法。
创建自己的视图子类让你可以精确控制界面元素的外形和功能。为了对这种控制有个大概的印象,下面是一些例子说明你可以用它们做什么:
·
你可以创建一个完全自定义绘制的视图类型,比如一个用
2D
图形绘制的“音量控制”旋钮,使它看上去像一个模拟电子件。
·
你可以把一组视图组件组合进一个单独的组件里,也许像一个组合框(是弹出列表和自由输入的文本段的组合),一个双窗格选择器控件(一个左窗格和右窗格,里面各有一个列表,你可以选择哪个项应该在哪个列表中),如此等等。
·
你可以重写一个编辑文本
EditText
组件的绘制方式(记事本指南
Notepad Tutorial
中使用这个方法创建了一个条纹状的记事本页面效果)。
·
你可以捕获其他事件如按键然后以自定义的方式处理。(比如一个游戏所做的那样)
下面的章节解释了怎么去创建自定义视图和在你的应用程序中使用它们。详细的参考信息,请查看视图
View
类。
基本方法
The Basic Approach
下面是关于怎么样开始创建自定义视图组件的一个概要性的总体描述:
1.
扩展一个现有的视图类然后子类化它。
2.
重写父类中的一些方法。这些方法以“
on
”开始,比如
onDraw()
, onMeasure()
,和
onKeyDown()
。这和活动或列表活动中为生命周期和其他功能钩子重写
on…
事件类似。
3.
使用你的新扩展类。这些完成后,你的扩展类就可以替代那个基础视图了。
提示
:
扩展类可以被定义成使用它们的活动的内部类。这样活动可以控制对它们的访问,这很有用但并非必须如此(也许你想在应用程序里创建一个使用更广的公共视图)。
完全自定义组件
Fully Customized Components
完全自定义组件可以用来创建你想要的图形组件。也许是一个图形
VU
表看起来像一个老式的模拟计量器,或者是一个伴唱字幕,上面有一个弹球随着文字移动这样你就可以在卡拉
OK
机上跟唱。无论哪种方式,你想要的东西都是内置组件所不能完成的,不管你怎么组合它们。
幸运的是,你可以简单的按照你的意愿来创建组件,除非你想不到,或者受限于屏幕尺寸,以及可用电源(记住,最终你的应用程序得运行在比桌面工作站电源少得多的设备上)。
要创建一个完全自定义组件:
1.
毫无意外,你可以扩展的最通用的视图是
View
,
因此你通常从扩展它开始创建你的超级组件。
2.
你可以供应一个构造器从
XML
中读取属性和参数,你也可以消费你自己的属性和参数(也许是
VU
表的颜色和范围,或者指针的宽度和阻尼,等)
3.
你可能也想在你的组件中创建自己的事件侦听器,属性访问和修改器,以及其它可能更复杂的行为。
4.
你将几乎肯定要重写
onMeasure()
而且也很可能需要重写
onDraw()
。
如果你希望这个组件显示一些东西。两者都有缺省行为,缺省的
onDraw()
方法不做任何事情,而缺省
onMeasure()
方法将总是设置一个
100x100
的尺寸
-
这或许不是你想要的。
5.
其他
on...
方法也可以按照要求重写。
扩展
onDraw()
和
onMeasure() Extend
onDraw()
and
onMeasure()
onDraw()
方法传给你一个画布
Canvas
对象,你可以在上面实现任何你想要的东西:
2D
图形,其它基础或自定义组件,风格文本,或其他任何你能想到的。
注意
:
这不适用于
3D
图形。如果你想使用
3D
图形,你必须扩展
SurfaceView
而不是
View
,并且从一个单独的线程中绘制。参见
GLSurfaceViewActivity
示例以了解更多细节。
.
onMeasure()
会被用得更多一点。
onMeasure()
是你的组件和它的容器之间绘制约定的关键部分。
onMeasure()
应该被重写来有效和准确的报告它所包含部分的尺寸。但是由于父类的一些限制性要求(被传递给
onMeasure()
方法)和以宽度和高度(一旦已经被计算出来)调用
setMeasuredDimension()
方法的要求,这将变得稍微复杂一些。如果你从一个重写的
onMeasure()
方法中调用这个方法失败,结果将返回一个测量时异常。
概要而言,实现
onMeasure()
看起来如下:
1.
给定宽度和高度测量规格
(
widthMeasureSpec
和
heightMeasureSpec
,两者都是代表维度的整数编码
)
来调用重写的
onMeasure
方法,这应该被当作度量上的限制性要求。完整的参考在
View.onMeasure(int, int)
文档中,这篇参考文档还很好的描述了整个测量操作)。
2.
你的组件的
onMeasure()
方法应该计算一个宽度和高度用来绘制这个组件。它应该尽可能留在传递的规格所指定的范围里,尽管它可以选择超出它们(这样的话,父类可以选择处理方式,包括裁剪,滚动,抛出异常,或者要求
onMeasure()
再试一次,也许会使用不同的度量规格。)
3.
一旦宽度和高度被计算出来,这个
setMeasuredDimension(int width, int height)
方法必须以计算出来的度量来调用。如果调用不成功,则将抛出异常。
下面是框架在视图上调用的一些其他基本方法的汇总:
类别 |
方法 |
描述 |
创建 |
构造器 |
有一种构造器形式是在从代码里创建视图时被调用,另一种是从一个布局文件中扩充视图时被调用。第二种形式应该解析并运用任何定义在布局文件中的属性。 |
当一个视图及其所有子项已经在 |
||
布局 |
用来决定这个视图及其所有子项的尺寸要求。 |
|
当这个视图应该为它所有的子项分配一个尺寸和位置的时候调用。 |
||
当这个视图的尺寸被改变时被调用。 |
||
绘画 |
当视图需要绘制其内容时被调用。 |
|
事件处理 |
当一个按键事件发生时被调用。 |
|
当一个按键释放事件发生时调用。 |
||
当一个跟踪球动作事件发生时被调用。 |
||
当一个触摸屏动作事件发生时被调用。 |
||
Focus |
当视图获取或丢失焦点时被调用。 |
|
当包含视图的窗口获取或丢失焦点时被调用。 |
||
Attaching |
当视图被附着到一个窗口时被调用。 |
|
当视图从一个窗口拆分开时被调用。 |
||
当包含视图的窗口的可见性发生改变时被调用。 |
一个自定义视图示例
A Custom View Example
这个在
API Demos
中的
CustomView
例子提供了自定义视图的示范。这个自定义视图定义在
LabelView
类中。
LabelView
例子说明了自定义组件的很多不同方面:
·
为一个完全自定义组件扩展视图类。
·
参数化的构造器,采用视图扩充参数(这些参数定义在
XML
中)。其中一些被传递给视图超类,但更重要的是,有一些自定义属性被
LabelView
所用。
·
基本的公共方法来设置一个
label
组件你所期望的类型,比如
setText(), setTextSize(), setTextColor()
等等。
·
一个重写的
onMeasure
方法来决定和设置这个组件的绘制尺寸。(注意在
LabelView
中,实际工作是通过一个私有的
measureWidth()
方法来完成的。)
·
一个重写的
onDraw()
方法来把标签(
label
)画到提供的画布
canvas
中。
method to draw the label onto the provided canvas.
你可以在
custom_view_1.xml
中查阅一些使用范例。特别是,你可以看到
android:
命名空间参数和自定义
app:
命名空间参数的一个混合。这些
app:
参数是
LabelView
认识和使用的,并被定义在
R
资源类的一个可格式化的内部类里。
复合控件
Compound Controls
如
果你不想创建一个完全自定义的组件,但希望由一组已有控件来组装成一个可复用组件,那么创建一个复合组件(或复合控件)可以满足要求。在一个小容器里,把
一些更原子的控件(或视图)整合进一个逻辑项目组中,从而可以被当作单个控件来对待。比如,一个组合框,可以被看作是一个单行文本编辑控制和一个附有弹出
列表的相邻按钮的组合。如果你按下这个按钮并从列表中选择一些项,它将被生成到文本编辑域中,不过用户也可以直接在文本编辑域进行输入如果愿意的话。
在
Android
里,事实上有另外两种视图也是这样做的:
Spinner
和
AutoCompleteTextView
,不过,组合框的概念比较容易理解。
为了创建一个复合组件:
1.
通常的起点是某种类型的布局,因此创建一个扩展布局的类。可能在组合框例子中,我们会使用一个水平方向的线性布局
LinearLayout
。记住其它布局可以被嵌套在里面,这样这个复合组件可以任意复杂和结构化。注意就像一个视图,你既可以使用声明(基于
XML
)方式来创建这个包含的组件,也可以通过编码在程序中嵌入它们。
2.
在这个新类的构造函数中,采用任何超类期望的参数,并首先传递给超类构造函数。然后你可以建立用于新组件的其它视图;这就是你将创建
EditText
域和
PopupList
的地方。注意你还可能需要引入你自己的属性和参数到
XML
中,这些将被提取出来并为你的构造函数所用。
3.
你还可以为包含的视图可能产生的事件创建侦听器,比如,一个列表项点击侦听器的方法,用来在一个列表项被选中时更新相应的文本编辑框内容。
4.
你可能还想通过访问和修改函数来创建自己的特性,比如,允许
EditText
值可以被初始化并在需要时被查询到。
5.
在扩展一个布局时,你不需要重写
onDraw()
和
onMeasure()
方法,因为布局会有缺省行为。不过,如果需要的话,你同样可以重写它们。
6.
你可能会重写其它
on...
方法,比如
onKeyDown()
,当一个特定键被按下时可能会从弹出列表中选择某个特定的默认值。
总之,基于布局创建自定义控件有如下一些好处,包括:
·