Android_文档学习_Building Custom Components


Building Custom Components

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类型,例如一个用2D图像给出的"声音控制"把手 , 像一个类似物的电子控制.
  • 你可以联合一组View组件放到一个新的单一的组件中,做出个类似组合框(弹窗装置列表 和自由的文本输入域), 一个多面板的选择控制器 (有左边和右边的每个都有面板)(一个左右窗格同一个表在每个,你可以重新指派哪个项目)
    ), 等待.
  • 你也可以重写EditText组件在屏幕上出现的方式(Notepad向导用这个效果不错,用来创建一连串页面).
  • 你也可以捕捉其他事件,像按键,然后用某种自定义的方式处理它们.


The Basic Approach(基本的方法)

下面是从一个高层次的视角来看你需要知道如何开始创建你自己的View组件:

  1. Extend an existing View class or subclass with your own class.(这句不知道怎么翻译更好,就不翻译了)
  2. Override父类的一些方法.构造方法通常以'on '开头, 例如, onDraw() , onMeasure() , and onKeyDown() . 这是类似在...事件在活动或类扩展,你重写为生命周期,其他功能性回调.
  3. 使用你的新的扩展类. 一旦完成,你的新的扩展类就能用于替换原有的VIEW了.

额外的: 扩展类可被定义为activity的内部类使用 .那是非常有用的,因为它控制它们的权限,但不是必要的(可能你想创建一个新的公共View为更广泛的使用你的应用程序).

Fully Customized Components

完全自定义的组件可用于创建任何时候你想让它显示的图形的组件.也许一个图解的声量计看起来像一个旧的模拟计量仪.或者一个单一的文本视图,如果一个反弹球跟着的话,你就能像卡拉ok机一样使用. Either way, you want something that the built-in components just won't do, no matter how you combine them.

创建一个完全自定义的组件:

  1. 最常用的你可以继承的view是,毫无疑问是View,因而你通常会通过继承它来创建新的父组件.
  2. 你可以提供一个能从xml中获取属性和参数的构造方法,而且你也可以销毁你自己的这些属性和参数.
  3. 你可能想要创建你自己的event监听者,拥有权限和修改,而且也有可能有更精巧的方法在你的组件类中.
  4. 你几乎可以确定你想要重写onMeasure()方法,而且很可能需要重写onDraw()方法,如果你想要这个组件显示一些东西的话.当它们两个方法都是默认的时候,默认的onDraw()方法什么都不会做,而且默认的onMeasure()方法永远都会设置100*100的size--你可能并不期望的大小.
  5. 其他on...方法也可能需要被overriden.

Extend 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中调用的标准方法的总结:

Category Methods Description
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.
onFinishInflate() Called after a view and all of its children has been inflated from XML.
Layout onMeasure(int, int) Called to determine the size requirements for this view and all of its children.
onLayout(boolean, int, int, int, int) Called when this view should assign a size and position to all of its children.
onSizeChanged(int, int, int, int) Called when the size of this view has changed.
Drawing onDraw(Canvas) Called when the view should render its content.
Event processing onKeyDown(int, KeyEvent) Called when a new key event occurs.
onKeyUp(int, KeyEvent) Called when a key up event occurs.
onTrackballEvent(MotionEvent) Called when a trackball motion event occurs.
onTouchEvent(MotionEvent) Called when a touch screen motion event occurs.
Focus onFocusChanged(boolean, int, Rect) Called when the view gains or loses focus.
onWindowFocusChanged(boolean) Called when the window containing the view gains or loses focus.
Attaching onAttachedToWindow() Called when the view is attached to a window.
onDetachedFromWindow() Called when the view is detached from its window.
onWindowVisibilityChanged(int) Called when the visibility of the window containing the view has changed.

Compound Controls

如果你不想创建一个完全自定义的组件,而是想要把一些用重用的组件组成一个已存在的控制器的组.那么创建一个复合的组件可能会满足你的需求.简而言之,这种方式将一系列更加原子控制器(或者views)带入一个可被当作一个单一items的逻辑组中.例如,一个组合框可被当作是一个单一的EditText域和一个邻近的按钮在一个附上弹出列表弹出列示的联合体.如果你按下按钮或者从list中选择某个,它会弹出一个EditText域,但是用户也可以直接在EditText中输入如果他们想的话.

在android中,这里有两个其他的VIEWS会乐意这样做:Spinner AutoCompleteTextView ,但不管怎样,组合框的概念更容易理解例子.

创建一个复合组件:

  1. 开始通常是某种类型的布局, 因而创建一个继承一个layout的类. 也许在组合框例子中我们可以使用horizontal orientation给LinearLayout.别忘了其他布局都可以在里面扩充.因而这个复合组件可被任意的综合和构造.就像对一个Activity操作一样,你既可以用xml的方式,也可以用代码的形式.
  2. 在新的类的构造方法中.获取父类想要的任意参数,然后首先用父类的构造方法传给它.然后你可以用你的新组件设置其他views;这里就是你创建editiText域和弹出列的地方.注意,你还要放入你自己的属性和参数到xml.这样你才能用你自己的构造函数使用它们.
  3. 你也可以创建你包含的views可能产生的事件的监听者.例如,为list item点击的监听方法,来监听更新的editText的内容.
  4. 你也可能需要创建你自己的权限和修改者的属性.例如.允许EditText()的值在组件中被具体的设置和当需要的时候它内容被查询.
  5. 这里继承一个layout.你不需要重写onDraw()和onMeasure()方法因为这个layout会有默认的方式工作.然而,如果你需要你也可以重写.
  6. 你也可以重写其他on...方法.像onKeyDown() ,从而当特定的按键被按下的时候,从一个组合框的弹出式列表中选择确定的默认值.

Modifying an Existing View Type

这里有一种更容易而且在任何情况下都非常有用的的方式来创建一个自定义View.如果这里存在一个和你想要的组件很类似的组件,你可以仅仅继承那个组件然后仅仅重写你想要改变的behavior.你可以在一个完全自定义的组件中做所有的这些事,但是要在View等级中一个更加具体的类中开始,你就需要获得许多behavior来准确阐释你想要.

例如,sdk中包括的一个 NotePad application例子.这个演示了用android平台的许多视角,其中继承了一个EditText View用于产生一个有线条的notepad.这并不是一个完美的例子,而且api里这样做,可能改变早期的预览,但是它确实演示了这种规则.

如果你还没有很好的准备好的话,引入NotePad的列子到elicpse中.特别要看看在NoteEditor.java文件中 MyEditText的定义.

一些需要注意的点:

1.定义

这个类应该被这样定义

public static class MyEditText extends EditText

  • 它作为内部类被定义在NoteEditor activity中,但它是public的,因而如果需要它可以像NoteEditor.MyEditText一样, 由来自外部的NoteEditor类访问.
  • 它是static的,意味着它不会产出所谓的综合方法来允许它被父类访问数据,那样就意味着它就像一个分开的类而不健壮的与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.

  • The other attributes and parameters in the definition are the ones passed into the custom component constructor, and then passed through to the EditText constructor, so they are the same parameters that you would use for an EditText view. Note that it is possible to add your own parameters as well, and we will touch on this again below.

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.

你可能感兴趣的:(component)