Creating a View Class

0x00、 创建一个 View 类

一个设计优秀的自定义 view 和其他的自定义类非常想像。 它包括一个特定的方法集以及一个简单的使用接口,它使用 CUP 和内存的效率高,并且速度快。 作为一个自定义 view 除了是一个设计优秀的类,还应该:

  • 符合 Android 标准。

  • 提供在 XML 布局文件运行良好的自定义风格属性。

  • 发送可达的事件。

  • 能兼容不同的 Android 版本。

Android 矿建提供了可以满足一切需求的一系列基础的类和 XML 标签帮助你创建一个 view。 这篇文字介绍如何使用 Android framework 创建 view 类的核心功能。

在开始学习本文之前你需要先了解一下:自定义组件

0x01、 View 的子类

Android framework 中的所有所有的 view 类都是 View 的子类。 你的自定义 view 也能直接继承 View ,或者你为了节省时间也可以集成其他已经存在的 view 的子类,例如 Button 。

要允许 Android Strudio 和你的类进行交互,你至少需要提供一个用于接收 Context 和一个 AttributeSet 的构造方法。 这个构造方法允许 layout 编辑器创建和编辑你 view 的实例。

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

0x02、 定义自定义属性

要把 view 构建到你的 UI ,你需要在 layout 文件中通过一个 XML 元素和一些属性来控制 view 的外观和行为。 写的好的 view 也能通过 XML 定义风格。 要想你的 view 具备这些能力,你需要:

  • 在资源文件众多 <declare-styleable> 节点下定义 view 的自定义属性。

  • 在你的 XML layout 文件中为你定义的属性设置值。

  • 在运行时检索属性和值。

  • 将检索到的属性和值应用到你的 view 中。

要定义自定义属性,想你的项目中添加 <declare-styleable> 资源。 就要将该自定义资源内容添加到 /res/values/attrs.xml 文件。 这里有一个 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>

上述代码在一个名为 PieChar 的风格实体中声明了两个自定义属性,showTextlabelPosition。 按照惯例,这个风格实体中的 name 字段和自定义 view 中的相同。 虽然没有强制要求遵循惯例,许多流行代码的编辑者尊徐这个惯例提供代码。

一旦你定义了自定义属性,你就可以在 layout 文件中的 XML 语法中像内建属性一样使用他们了。 唯一的不同是你自定义的属性术语不同的命名控件。 不是命名空间:http://schemas.android.com/apk/res/android;他们属于命名空间:http://schemas.android.com/apk/res/[your package name]。例如,使用定义域 PieChar 的属性:

<?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>
In Gradle projects, always use http://schemas.android.com/apk/res-auto for custom attributes less... (Ctrl+F1) 
In Gradle projects, the actual package used in the final APK can vary; for example,you can add a .debug package suffix in one version and not the other. Therefore, you should not hardcode the application package in the resource; instead, use the special namespace http://schemas.android.com/apk/res-auto which will cause the tools to figure out the right namespace for the resource regardless of the actual package used during the build.

为了避免不得不重复那么长的命名空间 URL ,这里使用了 xmlns 指令。 这个指令为命名空间为 http://schemas.android.com/apk/res/com.example.customviews 分配的是 custom 这个别名 。 你能选择任何你喜欢的别名。

注意填写到 XML tag 中的自定义 view 的名字。 它是自定义 view 的完全限定名。 如果你的 class 是一个内部类,你必须进一步指明它。 例如, PieChart 类有一个内部类 PieView 。 要使用这个类中的自定义属性,你需要使用 tagcom.example.customviews.charting.PieChart$PieView

0x03、 应用自定义属性

当一个 view 被从一个 XML 布局文件创建,在 XML 文件中对应 tag 先面的所有属性也被读取到 bundle 中并作为 AttributeSet 被发送到 view 的构造函数了。 因此可以直接从 AttributeSet 中读取值,这么做有一些劣势:

  • 属性值所引用的资源未被解析

  • 风格未被应用

相反,将 AttributeSet 传入 obtainStyledAttribute()。 这个方法可以传回一个 TypedArray 其中包含了已经被解析的引用和样式。

为了开发者能便利的调用 obtainStyledAttributes() Android 资源编译器做了大量的工作。 资源文件中的每一个 <declare-styleable> 资源,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 对象是共享资源,在使用完成后必须释放。

0x04、 增加属性和事件

属性是控制 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 的,只有这样系统才能知道 view 需要重绘。 同样,如果对 view 的修改可能影响到 view 的尺寸和形状,你需要请求一个新的布局。 忘记调用这些方法将会造成南移发现你的 BUG 。

自定义 view 也应该支持事件监听。 例如 PieChart 暴露一个名为 OnCurrentItemChanged 自定义事件用于通知监听者用户已经旋转饼图到一个新的饼片。

对外暴露属性和事件是一件很容易被忽略的事情,特别是只有你一个人作为该自定义 view 的使用者的时候。 花一些时间考虑和定义你 view 的接口减少未来维护成本吧。

0x05、 Design For Accessibility

你自定义的 view 应该支持更广泛的用户。 其中也包含无法看到或者无法触摸屏幕的残疾人。 要支持残疾人你需要:

  • 通过 android:contentDescription 属性标识你的输入属性。

  • 当适当的时候通过 sendAccessibilityEvent() 方法发送无障碍事件。

  • 支持备用控制器,例如 D-Pad 和 触摸球。

关于无障碍 view 的更多信息请查看 Android 开发者指导中的 Making Applications Accessible

Last updated