创建视图类

试用 Compose 方式
Jetpack Compose 是推荐用于 Android 的界面工具包。了解如何在 Compose 中使用布局。

设计良好的自定义视图与任何其他精心设计的类一样。它通过一个简单的接口封装一组特定的功能,高效地使用 CPU 和内存,等等。除了是精心设计的类之外,自定义视图还必须执行以下操作:

  • 符合 Android 标准。
  • 提供可与 Android XML 布局配合使用的自定义可设置样式属性。
  • 发送无障碍事件。
  • 与多种 Android 平台兼容。

Android 框架提供了一组基类和 XML 标记,帮助您创建满足所有这些要求的视图。本课将介绍如何使用 Android 框架创建视图类的核心功能。

如需了解其他信息,请参阅自定义视图组件

子类化视图

Android 框架中定义的所有视图类都会扩展 View。自定义视图也可以直接扩展 View,也可以通过扩展某个现有视图子类(如 Button)来节省时间。

若要允许 Android Studio 与您的视图交互,您必须至少提供一个接受 ContextAttributeSet 对象作为参数的构造函数。此构造函数允许布局编辑器创建和编辑视图的实例。

Kotlin

class PieChart(context: Context, attrs: AttributeSet) : View(context, attrs)

Java

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

定义自定义属性

如需向界面添加内置 View,请在 XML 元素中指定它,并使用元素属性控制其外观和行为。您还可以使用 XML 添加自定义视图并为其设置样式。如需在自定义视图中启用此行为,请执行以下操作:

  • <declare-styleable> 资源元素中定义视图的自定义属性。
  • 为 XML 布局中的属性指定值。
  • 在运行时检索属性值。
  • 将检索到的属性值应用于视图。

本部分介绍了如何定义自定义属性并指定其值。下一部分将介绍如何在运行时检索和应用值。

如需定义自定义属性,请向项目添加 <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>

此代码声明了两个自定义属性(showTextlabelPosition),它们属于一个名为 PieChart 的可设置样式实体。按照惯例,可设置样式的实体的名称与定义自定义视图的类的名称相同。虽然无需遵循此惯例,但许多热门代码编辑器都依赖此命名惯例来提供语句补全功能。

定义自定义属性后,您可以像使用内置属性一样在布局 XML 文件中使用它们。唯一的区别是您的自定义属性属于不同的命名空间。它们不属于 http://schemas.android.com/apk/res/android 命名空间,而是属于 http://schemas.android.com/apk/res/[your package name]。例如,下面展示了如何使用为 PieChart 定义的属性:

<?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-auto">
 <com.example.customviews.charting.PieChart
     custom:showText="true"
     custom:labelPosition="left" />
</LinearLayout>

为避免重复使用长命名空间 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 类如何读取其属性,如下所示:

Kotlin

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()
        }
    }
}

Java

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);
       textPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
   } finally {
       a.recycle();
   }
}

请注意,TypedArray 对象是共享资源,必须在使用后回收。

添加媒体资源和事件

属性是控制视图行为和外观的强大方式,但只有在视图初始化时才能读取它们。如需提供动态行为,请为每个自定义属性公开一个属性 getter 和 setter 对。以下代码段展示了 PieChart 如何公开名为 showText 的属性:

Kotlin

fun isShowText(): Boolean {
    return mShowText
}

fun setShowText(showText: Boolean) {
    mShowText = showText
    invalidate()
    requestLayout()
}

Java

public boolean isShowText() {
   return mShowText;
}

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

请注意,setShowText 会调用 invalidate()requestLayout()。这些调用对于确保视图可靠行为至关重要。您需要在视图属性发生任何可能会改变其外观的更改后使视图失效,以便系统知道需要重新绘制它。同样,如果属性发生的更改可能会影响视图的尺寸或形状,您就需要请求新的布局。忘记这些方法调用可能会导致难以发现的错误。

自定义视图还必须支持事件监听器来传达重要事件。例如,PieChart 公开了一个名为 OnCurrentItemChanged 的自定义事件,以通知监听器:用户旋转了饼图以将焦点放在新的饼图切片上。

公开属性和事件是很容易忘记的,尤其是当您是自定义视图的唯一用户时。 花些时间仔细定义视图接口可以降低未来的维护成本。一条很好的做法是始终公开任何会影响自定义视图的可见外观或行为的属性。

无障碍设计

您的自定义视图必须支持广泛的用户。包括看不到或使用触摸屏的残障用户。如需为残障用户提供支持,请执行以下操作:

  • 使用 android:contentDescription 属性为输入字段添加标签。
  • 根据需要调用 sendAccessibilityEvent() 来发送无障碍事件。
  • 支持备用控制器,例如方向键或轨迹球。

如需详细了解如何创建无障碍视图,请参阅 让应用使用起来更没有障碍