Android提供了一套精巧的强大的组件化的模型供你创建你的UI,它们是基于layout的View和ViewGroup的类.这个平台提供了许许多多的已经建好的View和ViewGroup子类-被称为widgets 和 layouts,正如所预期的那样.你可以用这些构造你自己的UI.
一部分可用的widgets包括 Button
, TextView
, EditText
, ListView
, CheckBox
, RadioButton
, Gallery
, Spinner
,和更多特殊用途的widgets: AutoCompleteTextView
, ImageSwitcher
, and TextSwitcher
.
在这些可用的layouts中,有 LinearLayout
, FrameLayout
, RelativeLayout
, 和其他一些.
如果这些预制的widgets和layouts没有适合你需求的,你可以创建你自己的View子类.如果你需要对现有的widget或layout做一些小的调整的话,你只需要简单的override那个widget或layout的一些方法.
创建你自己的View子类使你能更精确的控制一个屏幕元素的显现和功能.为了给你一些控制你自定义的views的想法,下面给出了一些你能做什么的例子:
下面是从一个高层次的视角来看你需要知道如何开始创建你自己的View组件:
View
class or subclass with your own class.(这句不知道怎么翻译更好,就不翻译了)on
'开头, 例如, onDraw()
, onMeasure()
, and onKeyDown()
. 这是类似在...事件在活动或类扩展,你重写为生命周期,其他功能性回调. 额外的: 扩展类可被定义为activity的内部类使用 .那是非常有用的,因为它控制它们的权限,但不是必要的(可能你想创建一个新的公共View为更广泛的使用你的应用程序).
完全自定义的组件可用于创建任何时候你想让它显示的图形的组件.也许一个图解的声量计看起来像一个旧的模拟计量仪.或者一个单一的文本视图,如果一个反弹球跟着的话,你就能像卡拉ok机一样使用. Either way, you want something that the built-in components just won't do, no matter how you combine them.
创建一个完全自定义的组件:
onDraw()
and onMeasure()
onDraw()方法传给你一个Canvas,在上面你可以实现你想要做的任何事:2D图形,其他标准或者自定义的组件,风格文本,或者其他你能想到的任何事.
注意:这个不支持3D图形.如果你想用3D图形的话,你必须继承SurfaceView而不是继承View,然后从一个另外的线程得到.
onMeasure()方法有点复杂难懂.onMeasure()方法是翻译你的组件和你组件的容器之间的关系的重要部分.onMeasure()方法需要被有效地重写和精确的报告它包含的部分的测量结果.一旦从父类的需求上限和用测量的宽度和高度被setMeasuredDimension()方法调用,就变得有点复杂了.如果你没有调用这个重写了的onMeasure()方法,结果会在测量的时候产生一个异常.
在高层次上实现onMeasure()方法看起来是这样的:
1.重写的onMeasure()方法被调用的时候使用的具体测量的宽度和高度,需要被当作你要生成的测量高度和宽度的约束的必要条件.一个对这类具体约束的完全引用,需要在 View.onMeasure(int, int)文档中存在.
2. 你组件的onMeasure()方法需要计算传给组件的测量的宽度和高度.它必须保持为具体传过来的值,尽管它可以选择超过它们(这种情况下,父类可以选择做什么,包括clipping, scrolling, throwing an exception, or asking the onMeasure()
to try again, 也许用不同的测量规范).
3.一旦宽度和高度被计算后,setMeasuredDimension(int width, int height)方法必须用计算的测量值调用.不这样做,也会产生一个异常.
下面是其中的一些framework会在Views中调用的标准方法的总结:
Creation | Constructors | There is a form of the constructor that are called when the view is created from code and a form that is called when the view is inflated from a layout file. The second form should parse and apply any attributes defined in the layout file. |
|
Called after a view and all of its children has been inflated from XML. | |
Layout | |
Called to determine the size requirements for this view and all of its children. |
|
Called when this view should assign a size and position to all of its children. | |
|
Called when the size of this view has changed. | |
Drawing | |
Called when the view should render its content. |
Event processing | |
Called when a new key event occurs. |
|
Called when a key up event occurs. | |
|
Called when a trackball motion event occurs. | |
|
Called when a touch screen motion event occurs. | |
Focus | |
Called when the view gains or loses focus. |
|
Called when the window containing the view gains or loses focus. | |
Attaching | |
Called when the view is attached to a window. |
|
Called when the view is detached from its window. | |
|
Called when the visibility of the window containing the view has changed. |
如果你不想创建一个完全自定义的组件,而是想要把一些用重用的组件组成一个已存在的控制器的组.那么创建一个复合的组件可能会满足你的需求.简而言之,这种方式将一系列更加原子控制器(或者views)带入一个可被当作一个单一items的逻辑组中.例如,一个组合框可被当作是一个单一的EditText域和一个邻近的按钮在一个附上弹出列表弹出列示的联合体.如果你按下按钮或者从list中选择某个,它会弹出一个EditText域,但是用户也可以直接在EditText中输入如果他们想的话.
在android中,这里有两个其他的VIEWS会乐意这样做:Spinner
和 AutoCompleteTextView
,但不管怎样,组合框的概念更容易理解例子.
创建一个复合组件:
onKeyDown()
,从而当特定的按键被按下的时候,从一个组合框的弹出式列表中选择确定的默认值.这里有一种更容易而且在任何情况下都非常有用的的方式来创建一个自定义View.如果这里存在一个和你想要的组件很类似的组件,你可以仅仅继承那个组件然后仅仅重写你想要改变的behavior.你可以在一个完全自定义的组件中做所有的这些事,但是要在View等级中一个更加具体的类中开始,你就需要获得许多behavior来准确阐释你想要.
如果你还没有很好的准备好的话,引入NotePad的列子到elicpse中.特别要看看在NoteEditor.java文件中 MyEditText的定义.
一些需要注意的点:
1.定义
这个类应该被这样定义
public static class MyEditText extends EditText
NoteEditor.MyEditText一样,
由来自外部的NoteEditor类访问. EditText
,在本例中被自定义的View .当我们完成的时候,新的类就可适用于一个普通的EditText view.2.类的初始化
As always, the super is called first. Furthermore, this is not a default constructor, but a parameterized one. The EditText is created with these parameters when it is inflated from an XML layout file, thus, our constructor needs to both take them and pass them to the superclass constructor as well
3.重写方法
In this example, there is only one method to be overridden: onDraw()
— but there could easily be others needed when you create your own custom components.
For the NotePad sample, overriding the onDraw()
method allows us to paint the blue lines on the EditText
view canvas (the canvas is passed into the overridden onDraw()
method). The super.onDraw() method is called before the method ends. The superclass method should be invoked, but in this case, we do it at the end after we have painted the lines we want to include.
4.使用自定义组件
在res/layout文件中创建的note_editor.xml代码如下:
这个自定义的组件在xml中被当作一个通用的view创建.而且这个类用具体的包名引用.注意我们定义的这个内部类使用 NoteEditor$MyEditText记号在java 程序中被引用.
如果你的自定义View组件未被定义为一个内部类,那么你可以用另一种方式,声明这个View组件用xml元素名.然后剔除类的属性.例如:
Notice that the MyEditText
class is now a separate class file. When the class is nested in the NoteEditor
class, this technique will not work.
And that's all there is to it. Admittedly this is a simple case, but that's the point — creating custom components is only as complicated as you need it to be.
A more sophisticated component may override even more on...
methods and introduce some of its own helper methods, substantially customizing its properties and behavior. The only limit is your imagination and what you need the component to do.