0%

Android开发:创建自定义视图--创建一个View类

翻自:http://developer.android.com/training/custom-views/create-view.html 设计良好的定制视图与其他精心设计的类相似。它用一种易于使用的接口封装了一些特殊的方法集合,它高效使用内存和CPU等等。除了良好的设计以外,一个定制视图应该: 符合Android标准 提供定制的可在xml布局中使用的风格属性 发送可访问性事件(针对盲人等) 兼容多个Android平台 继承View类 Android框架里所有的视图类继续自View,你的自定义视图也可以继续View,或者你可以继承一个已经存在的View子类节约时间,像Button 为了能让ADT布局编辑器能使用你定制的View,你的View必须提供一个Context和 AttributeSet对象作为参数的构造方法(要在xml中用,就必须有这个构造方法)

class PieChart extends View {
    public PieChart(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

定义自定义属性 给你的UI添加一个内建的View,你可以在xml中通过指定它的属性,控制它的外观行为。写得好的自定义View也可以通过xml添加和设定风格,为了在你的自定义View中启用这个功能,你必须: 使用元素为你的自定义View定制属性 在xml布局中指定属性值 运行时获取属性值 在你的View中应用获取到的属性值 本节讨论如何定义定制属性并且指定他们的值,下节处理在运行时获取并应用这些值 通过添加来定义定制属性,通常把这些资源放在res/values/attrs.xml文件中,下面是一个例子:

<resources>
   <declare-styleable name="PieChart">
       <attr name="showText" format="boolean" />
       <attr name="labelPosition" format="enum">
           <enum name="left" value="0"/>
           <enum name="right" value="1"/>
       </attr>
   </declare-styleable>
</resources>

这些代码声明了两个自定义属性,showText和labelPosition,属于一个叫PieChart的styleable实体。实体的名称,按照惯例,与自定义View类名相同,尽量没有严格要求必须遵循这个惯例,大多数代码编辑器依赖这个命名惯例提供代码提示。 一旦你定义了自定义属性,你可以就像是内建的属性一样在xml布局中使用。唯一的区别是,你的自定义属性属于一个不同的命名空间,他们属于http://schemas.android.com/apk/res/\[包名\]命名空间,而不是系统内置的http://schemas.android.com/apk/res/android。下面是使用属性的方法:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
 <com.example.customviews.charting.PieChart
     custom:showText="true"
     custom:labelPosition="left" />
</LinearLayout>

为了避免重复长命名空间URI,例子使用了xmlns指令,该指令声明http://schemas.android.com/apk/res/com.example.customviews命名空间的一个别名,你可以任何你喜欢的别名。 注意布局中的xml标签名,这是自定义View的全名(包括包名),如果你的View类是内部类,你必须使用它的外部类,举例来说,PieChart有一个叫PieView的内部类,如果要用PieView,你必须标签com.example.customviews.charting.PieChart$PieView。 应用自定义属性 当一个View从xml布局中创建时,xml中的所有属性都会从资源bundle里读取,并作为AttributeSet对象传给View的构造方法,尽管可以直接从AttributeSet里读取这些值,但这么做有一些不好的地方: 没有属性的资源引用不能被解析 样式没有应用到View上 作为代替,把AttributeSet传给obtainStyledAttributes(),这个方法返回一个已经解析好并应用样式的TypedArray数组。 为了让你更容易地调用obtainStyledAttributes(),Android资源编译器做了一大堆工作.res目录里的每一个 资源,自动生成的R.java文件都定义了一个属性id数组和属性在该数组中的索引集合。你要使用预编译的静态变量从TypedArray里读取属性。下面是PieChart类如何读取属性:

public PieChart(Context context, AttributeSet attrs) {
   super(context, attrs);
   TypedArray a = context.getTheme().obtainStyledAttributes(
        attrs,
        R.styleable.PieChart,
        0, 0);

   try {
       mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
       mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
   } finally {
       a.recycle();
   }
}

注意TypedArray对象是共享资源,你用完必须回收。 添加属性和事件 属性是控制View的行为和外观的强大工具,当在View初始化时,他们只能被读取。为了提供动态的行为,可以暴露一个属性的getter和setter方法。下面的代码段展示了PieChart如何暴露showText属性

public boolean isShowText() {
   return mShowText;
}

public void setShowText(boolean showText) {
   mShowText = showText;
   invalidate();
   requestLayout();
}

注意setShowText调用了invalidate()和requestLayout(),这些调用是至关重要的,以确保View表现可靠。你必须在改变可能引起View外观变化的属性后,使View无效,系统才能知道它需要被重绘。同样地,如果属性改变会影响尺寸或形状,你需要请求一个新的布局。少了这两行会产生难以预以发现的BUG。 自定义View也支持事件监听器与外界通讯。举例来说,PieChart暴露了一个叫OnCurrentItemChanged的自定义事件,来告诉监听者用户翻转了饼图,关注了一个新饼图。 忘记暴露属性和事件很容易,特别是自定义View只有自己用的时候,多花点时间小心定义你的View接口,减少未来维护成本。一个好的原则是,暴露你的View中所有影响外观和形为的属性。 无障碍设计 你的自定义View应该支持最广泛的用户,包括看不见,不能使用触摸屏的残障人士。为了这部分用户,你应该: 使用android:contentDescription给输入字段做标记 在适当的时候通过发送sendAccessibilityEvent()发送可访问性事件 支持备用控制器,如手柄,轨迹球。