一、简介
设计良好的自定义视图与任何其他精心设计的类一样。它通过一个简单的接口封装一组特定的功能,高效使用 CPU 和内存,诸如此类。除了是一个精心设计的类之外,自定义视图还必须执行以下操作:
- 符合 Android 标准。
- 提供适用于 Android XML 布局的自定义可设置样式属性。
- 发送无障碍事件。
- 与多种 Android 平台兼容。
Android 框架提供了一组基类和 XML 标记,以帮助您创建满足上述所有要求的视图。接下来将讨论如何使用 Android 框架创建视图类的核心功能。
您可以在自定义视图组件中找到更多信息。
二、子类化视图
Android 框架中定义的所有视图类都会扩展 View。您的自定义视图还可以直接扩展 View,或者您可以通过扩展某个现有视图子类(如 Button)来节省时间。
如需允许 Android Studio 与视图交互,您必须至少提供一个接受 Context 和 AttributeSet 对象作为参数的构造函数。此构造函数允许布局编辑器创建和编辑视图的实例。
bash
class PieChart : View {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}
三、定义自定义属性
如需向界面添加内置 View,请在 XML 元素中指定,并使用元素属性控制其外观和行为。您还可以使用 XML 添加自定义视图并为其设置样式如需在自定义视图中启用此行为,请执行以下操作:
- 在 资源元素中为视图定义自定义属性。
- 为 XML 布局中的属性指定值。
- 在运行时检索属性值。
- 将检索到的属性值应用于您的视图。
本部分将介绍如何定义自定义属性并指定其值。下一部分将介绍在运行时检索和应用值。
如需定义自定义属性,请向项目添加 资源。通常的做法是将这些资源放在 res/values/attrs.xml 文件中。下面是一个 attrs.xml 文件示例:
bash
<?xml version="1.0" encoding="utf-8"?>
<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>
为避免必须重复冗长的命名空间 URI,该示例使用了 xmlns 指令。此指令将别名 custom 分配给命名空间 http://schemas.android.com/apk/res/com.example.customviews。 您可以为命名空间选择所需的任何别名。
注意将自定义视图添加到布局的 XML 标记的名称。它是自定义视图类的完全限定名称。如果您的视图类是内部类,请使用视图外部类的名称进一步限定它。 例如,PieChart 类有一个名为 PieView 的内部类。如需使用此类中的自定义属性,请使用 com.example.customviews.charting.PieChart$PieView 标记。
四、应用自定义属性
通过 XML 布局创建视图时,XML 标记中的所有属性都会从资源包中读取,并作为 AttributeSet 传递到视图的构造函数中。 虽然可以直接从 AttributeSet 读取值,但这样做有一些弊端:
- 系统不会解析属性值中的资源引用。
- 因此不会应用样式。
请改为将 AttributeSet 传递给 obtainStyledAttributes()。 此方法会传回一个 TypedArray 数组,其中包含已解除引用并设置了样式的值。
Android 资源编译器做了大量工作,以便您更轻松地调用 obtainStyledAttributes()。对于 res/ 目录中的每个 <declare-styleable> 资源,生成的 R.java 会同时定义属性 ID 数组和一组常量(用于定义数组中每个属性的索引)。您可以使用预定义的常量从 TypedArray 中读取属性。以下代码展示了 PieChart 类如何读取其属性:
bash
init {
context.theme.obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0).apply {
try {
mShowText = getBoolean(R.styleable.PieChart_showText, false)
textPos = getInteger(R.styleable.PieChart_labelPosition, 0)
} finally {
recycle()
}
}
}
请注意,TypedArray 对象是共享资源,必须在使用后回收。
请注意 ,setShowText 会调用 invalidate() 和 requestLayout()。这些调用对于确保视图可靠运行至关重要。在对视图属性进行任何可能会改变其外观的更改后,您需要使该视图失效,以便系统知道需要重新绘制该视图。同样,如果属性的变化方式可能会影响视图的大小或形状,您需要请求新的布局。忘记这些方法调用可能会导致难以发现的 bug。自定义视图还必须支持事件监听器来传达重要事件。例如,PieChart 公开了一个名为 OnCurrentItemChanged 的自定义事件,以通知监听器用户旋转了饼图以将焦点放在新的饼图切片上。
公开属性和事件是很容易忘记的,尤其是当您是自定义视图的唯一用户时。花点时间仔细定义视图界面可以降低未来的维护成本。一种好的做法是始终公开任何会影响自定义视图的可见外观或行为的属性。
五、在设计时充分考虑无障碍功能
您的自定义视图必须支持众多用户。包括妨碍他们看到或使用触摸屏的残障用户。如需为残障用户提供支持,请执行以下操作:
- 使用 android:contentDescription 属性为输入字段添加标签。
- 根据需要调用 sendAccessibilityEvent() 来发送无障碍事件。
- 支持备用控制器,例如方向键或轨迹球。
如需详细了解如何创建易于访问的视图,请参阅 让应用使用起来更没有障碍。
六、PieChart.kt代码
bash
class PieChart(context: Context, attrs: AttributeSet) : View(context, attrs) {
private var mShowText: Boolean = false
private var textPos: Int = 0
init {
context.theme.obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0
).apply {
try {
mShowText = getBoolean(R.styleable.PieChart_showText, false)
textPos = getInteger(R.styleable.PieChart_labelPosition, 0)
} finally {
recycle()
}
}
}
fun isShowText(): Boolean {
return mShowText
}
fun setShowText(showText: Boolean) {
mShowText = showText
invalidate()
requestLayout()
}
}