构建自定义组件 收藏
Android中,你的应用程序程序与View类组件有着一种固定的联系,例如按钮(Button)、 文本框(TextView), 可编辑文本框(EditText), 列表框(ListView), 复选框(CheckBox), 单选框(RadioButton), 滚动条(Gallery), 微调器(Spinner), 等等,还有一些比较先进的有着特殊用途的View组件,例如 AutoCompleteTextView, ImageSwitcher和 TextSwitcher。除此之外,种类繁多的像 线性布局(LinearLayout), 框架布局(FrameLayout), 这样的布局组件(Layout)也被认为是View组件,他们是从View类派生过来的。
你的应用程序就是这些控制组件和布局组件以某种方式结合显示在屏幕上,一般来说这些组件对你来说基本够用,但是你也应该知道你是可以通过类继承创建属于自己的组件,一般可以继承像View、Layouts(布局组件)这样的组件,甚至可以是一些比较高级的控制类组件。下面我们说一下为什么要继承:
你可以为实现某种功能创建一个完全自定义风格的组件,例如用二维的图形创建控制组件实现声音的控制,就像电子控制一样。
你可以把几种组件结合形成一个新的组件,你的组件可能同时包含ComboBox(一个能输入的文本列表)和dual-pane selector control(左右两个List窗口,你可以分配窗口每一项的从属关系)等等。
你可以创建自己的布局组件(Layout)。SDK中的布局组件已经提供了一系列的选项让你打造属于自己的应用程序,但是高级的开发人员会发现根据现有的Layout组件开发新的Layout组件是很有必要的,甚至是完全从底层开发新的组件。
你可以覆盖一个现有组件的显示或功能。例如,改变EditText(可编辑文本)组件在屏幕上的显示方式(可以参考Notepad的例子,里面教你如何创建一个下划线的显示页面)。
你可以捕获像按键按下这样的事件,以一些通用的方法来处理这些事件(一个游戏的例子)。
为了实现某种目标你可能很有必要扩展一个已经存在的View组件,下面我们结合一些例子教你如何去做。
内容:
基本方法(The Basic Approach )
完全自定义组件(Fully Customized Components )
定制组件的例子(Customized Component Example )
组件的混合(或者控制类的混合) (Compound Components (or Compound Controls) )
修改现有组件(Tweaking an Existing Component )
小结(Go Forth and Componentize )
基本方法(The Basic Approach )
下面的一些步骤都比较概括,教你如何创建自己的组件:
让你的类(Class)继承一个现有的View 类或View的子类。
重载父类的一些方法:需要重载的父类方法一般以‘on’开头,如onDraw(), onMeasure()和 onKeyDown()等等。
这个在Activity 或则 ListActivity 派生中同样适用,你需要重载一些生命周期函数和一些其他功能性的HOOK函数。
使用你的继承类:一旦你的继承类创建完成,你可以在基类能够使用的地方使用你的继承类,但完成功能就是你自己编写的了。
继承类能够定义在activities里面,这样你能够方便的调用,但是这并不是必要的(或许在你的应用程序中你希望创建一个所有人都可以使用的组件)。
完全自定义组件(Fully Customized Components)
完全自定义组件的方法可以创建一些用于显示的图形组件(graphical components),也许是一个像电压表的图形计量器,或者想卡拉OK里面显示歌词的小球随着音乐滚动。无论那种方式,你也不能单纯的利用组件的结合完成,无论你怎么结合这些现有的组件。
幸运的是,你可以以你自己的要求轻松地创建完全属于自己的组件,你会发现不够用的只是你的想象力、屏幕的尺寸和处理器的性能(记住你的应用程序最后只会在那些性能低于桌面电脑的平台上面运行)。
下面简单介绍如何打造完全自定义的组件:
最为通用的VIEW类的父类毫无疑问是View类,因此,最开始你要创建一个基于此类的一个子类。
你可以写一个构造函数从XML文件中提取属性和参数,当然你也可以自己定义这些属性和参数(也许是图形计量器的颜色和尺寸,或者是指针的宽度和幅度等等)
你可能有必要写自己的事件监听器,属性的访问和修改函数和一些组件本身的功能上的代码。
如果你希望组件能够显示什么东西,你很有可能会重载 onMeasure() 函数,因而你就不得不重载 onDraw() 函数。当两个函数都用默认的,那么 onDraw() 函数将不会做任何事情,并且默认的 onMeasure() 函数自动的设置了一个100x100 —的尺寸,这个尺寸可能并不是你想要的。
其他有必要重载的on... 系列函数都需要重新写一次。
onDraw()和onMeasure()
onDraw()函数将会传给你一个 Canvas 对象,通过它你可以在二维图形上做任何事情,包括其他的一些标准和通用的组件、文本的格式,任何你可以想到的东西都可以通过它实现。
注意: 这里不包括三维图形如果你想使用三维的图形,你应该把你的父类由View改为SurfaceView类,并且用一个单独的线程。可以参考GLSurfaceViewActivity 的例子。
onMeasure() 函数有点棘手,因为这个函数是体现组件和容器交互的关键部分,onMeasure()应该重载,让它能够有效而准确的表现它所包含部分的测量值。这就有点复杂了,因为我们不但要考虑父类的限制(通过onMeasure()传过来的),同时我们应该知道一旦测量宽度和高度出来后,就要立即调用setMeasuredDimension() 方法。
概括的来讲,执行onMeasure()函数分为一下几个阶段:
重载的onMeasure()方法会被调用,高度和宽度参数同时也会涉及到(widthMeasureSpec 和heighMeasureSpec两个参数都是整数类型),同时你应该考虑你产品的尺寸限制。这里详细的内容可以参考View.onMeasure(int, int) (这个连接内容详细的解释了整个measurement操作)。
你的组件要通过onMeasure()计算得到必要的measurement长度和宽度从而来显示你的组件,它应该与规格保持一致,尽管它可以实现一些规格以外的功能(在这个例子里,父类能够选择做什么,包括剪切、滑动、提交异常或者用不同的参数又一次调用onMeasure()函数)。
一旦高度和宽度计算出来之后,必须调用setMeasuredDimension(int width, int height),否则就会导致异常。
一个自定义组件的例子(A Customized Component Example)
在 API Demos 中的CustomView提供了以一个自定义组件的例子,这个自定义组件在 LabelView 类中定义。
LabelView例子涉及到了自定义组件的方方面面:
首先让自定义组件从View类中派生出来。
编写带参数的构造函数(参数可以来源于XML文件)。这里面的一些处理都已经在View父类中完成,但是任然有些Labelview使用的自定义组件特有的新的参数需要处理。
一些标准的Public函数,例如setText(), setTextSize(), setTextColor()
重载onMeasure()方法来确定组件的尺寸(注意:在LabelView中是通过一个私有函数measureWidth()来实现的)
重载onDraw()函数把Lable显示在提供的canvas上。
在例子中,你可以通过custom_view_1.xml看到自定义组件LabelView的用法。在XML文件中特别要注意的是android:和app:两个参数的混合运用,app:参数表示应用程序中被认为是LabelView组件的个体,这些也会作为资源在R类中定义。
组件混合技术Compound Components (or Compound Controls)
如果你不想创建一个完全自定义的组件,而是由几个现有组件的组合产生的新的组件,那么混合组件技术就更加适合。简单的来说,这样把几个现有的组件融合到一个逻辑组合里面可以封装成一个新的组件。例如,一个Combo Box组件可以看作是是一个EditText和一个带有弹出列表的Button组件的混合体。如果你点击按钮为列表选择一项,
在Android中,其实还有其他的两个View类可以做到类似的效果: Spinner和AutoCompleteTextView,,但是Combo Box作为一个例子更容易让人理解。
下面简单的介绍如何创建组合组件:
一般从Layout类开始,创建一个Layout类的派生类。也许在Combo box我们会选择水平方向的LinearLayout作为父类。记住,其他的Layout类是可以嵌套到里面的,因此混合组件可以是任何组件的混合。注意,正如Activity一样,你既可以使用外部XML文件来声明你的组件,也可以嵌套在代码中。
在新的混合组件的构造函数中,首先,调用所有的父类的构造函数,传入对应的参数。然后可以设置你的混合组件的其他的一些方面,在哪创建EditText组件,又在哪创建PopupList组件。注意:你同时也可以在XML文件中引入一些自己的属性和参数,这些属性和参数也可以被你的混合组件所使用。
你也可以创建时间监听器去监听新组件中View类触发的事件,例如,对List选项单击事件的监听,你必须在此时间发生后更新你EditText的值。
你可能创建自己的一些属性,带有访问和修改方法。例如,允许设置EditText初始值并且提供访问它的方法。
在Layout的派生类中,你没有必要去重载onDraw()和onMeasure()方法,因为Layout会有比较好的默认处理。但是,如果你觉得有必要你也可以重载它。
你也可能重载一些on系列函数,例如通过onKeyDown()的重载,你可以通过按某个键去选择列表中的对应的值。
总之,把Layout类作为基类有下面几个优点:
正如activity一样,你也可以通过XML文件去声明你的新组件,或者你也可以在代码中嵌套。
onDraw()函数和onMeasure()函数是没有必要重载的,两个函数已经做得很好了。
你可以很快的创建你的混合组件,并且可以像单一组件那样使用。
混合组件的例子(Examples of Compound Controls)
In the API Demos project 在API Demos工程中,有两个List类的例子——Example 4和Example 6,里面的SpeechView组件是从LinearLayout类派生过来,实现显示演讲显示功能,对应的原代码是List4.java和List6.java。
调整现有组件(Tweaking an Existing Component)
在某些情况下,你可能有更简单的方法去创建你的组件。如果你应经有了一个非常类似的组件,你所要做的只是简单的从这个组件派生�