创建自定义视图组件

试用 Compose 方式
Jetpack Compose 是推荐在 Android 设备上使用的界面工具包。了解如何在 Compose 中使用布局。
<ph type="x-smartling-placeholder"></ph> Compose 中的自定义布局 →

Android 提供了一个复杂而强大的组件化模型,用于基于 基本布局类 ViewViewGroup。该平台包含 各种预构建的 ViewViewGroup 子类(称为 widget)和 分别用于构建界面。

可用的部分微件包括 ButtonTextViewEditTextListViewCheckBoxRadioButtonGallerySpinner,以及具有特殊用途的 AutoCompleteTextViewImageSwitcherTextSwitcher

可用的布局包括 LinearLayout, FrameLayout, RelativeLayout, 等。如需查看更多示例,请参阅 常见布局

如果所有预构建的 widget 或布局都无法满足您的需求,您可以创建自己的 View 子类。如果您只需要对现有 widget 进行小幅调整,或者 您可以为 widget 或布局创建子类并重写其方法。

通过创建自己的 View 子类,您可以精确控制外观和 函数。下面介绍了自定义视图可为您带来的控制力 下面列举了一些示例来说明您可以如何使用它们:

  • 您可以创建完全自定义呈现的 View 类型,例如“Volume 对照组”按钮,使用 2D 图形呈现,类似于模拟电子控件。
  • 您可以将一组 View 组件组合成一个新的单个组件,例如: 创建类似组合框(弹出列表和自由输入文本字段的组合)、 双窗格选择器控件(一个左右窗格,每个窗格都有一个列表,您可以在其中重新分配 等等。
  • 您可以替换 EditText 组件在屏幕上的渲染方式。通过 <ph type="x-smartling-placeholder"></ph> NotePad 示例应用使用此效果很好地创建了一个带线条的记事本页面。
  • 您可以捕获其他事件(例如按键),并以自定义方式处理这些事件,例如

以下部分介绍了如何创建自定义视图并在应用中使用它们。对于 请参阅 View 类。

基本方法

下面简要介绍了创建您自己的View所需了解的事项 组件:

  1. 使用您自己的类扩展现有的 View 类或子类。
  2. 替换父类中的某些方法。要替换的父类方法开头 on - 例如, onDraw(), onMeasure(), 和 onKeyDown()。 这类似于以下项目中的 on 事件: ActivityListActivity 替换生命周期和其他功能钩子。
  3. 使用您的新扩展类。完成后,您可以使用新的扩展类来代替 所依据的视图
。 <ph type="x-smartling-placeholder">

完全自定义的组件

您可以创建完全自定义的图形组件, 。例如,您需要一个看起来像老式模拟量表的图形 VU 计,或者是跟唱文本视图 跟着卡拉 OK 机一起唱歌,这个弹跳的球会随着歌词一起移动。你可能需要 内置组件无法执行的操作,无论你以何种方式组合使用。

幸运的是,您可以创建外观和行为如您所愿的组件,功能仅限于 屏幕大小和可用处理能力 完全由您自己决定 应用可能需要在功耗明显低于桌面系统的情况下运行 工作站。

如需创建完全自定义的组件,请考虑以下事项:

  • 您可以扩展的最通用的视图是 View,因此通常从扩展开始 以便创建新的超级组件。
  • 您可以提供一个构造函数,它可以从 XML 中获取属性和参数, 使用您自己的此类属性和参数,例如 VU 计的颜色和范围,或 针的宽度和阻尼
  • 您可能需要创建自己的事件监听器、属性访问器和修饰符,以及 在组件类中实现更复杂的行为
  • 您几乎肯定需要替换 onMeasure(),并且还可能需要 如果您希望组件显示内容,请替换 onDraw()。虽然这两种语言都具有 默认行为,默认的 onDraw() 不执行任何操作,而默认的 onMeasure() 始终会将尺寸设为 100x100,您可能并不希望这样设置。
  • 您还可以根据需要替换其他 on 方法。

扩展 onDraw() 和 onMeasure()

onDraw() 方法提供 Canvas,您可以 实现您想要的任何内容:2D 图形、其他标准或自定义组件、样式文本或 还有其他您能想到的事物吗?

<ph type="x-smartling-placeholder">

onMeasure() 涉及更多。onMeasure()至关重要 组件与其容器之间的渲染协定的onMeasure()必须是 替换后可高效、准确地报告其所含部分的测量结果。这是 而父级的限制要求则稍微复杂一些,后者传递到 onMeasure() 方法,并需要调用 setMeasuredDimension() 方法,并测量相应的宽度和高度。 计算。如果您不通过已替换的 onMeasure() 方法调用此方法, 会导致衡量时出现异常。

概括来讲,实现 onMeasure() 如下所示:

  • 使用宽度和高度调用替换的 onMeasure() 方法 这些规范被视为对宽度和高度的限制要求 测量出的测量数据。widthMeasureSpecheightMeasureSpec 参数都是表示维度的整数代码。对 相关规范要求的限制,请参阅 View.onMeasure(int, int) 本参考文档还介绍了整个测量操作。
  • 组件的 onMeasure() 方法会计算测量宽度和高度, 呈现组件所需的资源它必须尽量符合通过的规范 但也可能会超过它们在这种情况下,家长可以选择执行哪些操作,包括 裁剪、滚动、抛出异常或要求 onMeasure() 重试; 可能采用不同的衡量规范
  • 计算宽度和高度后,调用 setMeasuredDimension(int width, int height) 方法, 测量结果。否则会导致异常。

下面总结了框架对视图调用的其他标准方法:

类别 方法 说明
创建 构造函数 有一种构造函数形式,会在从代码创建视图时调用 以及从布局文件膨胀视图时调用的表单。第二种形式 解析和应用布局文件中定义的属性。
onFinishInflate() 在视图及其所有子项都从 XML 扩充之后调用。
布局 onMeasure(int, int) 调用以确定此视图及其所有子级的大小要求。
onLayout(boolean, int, int, int, int) 在此视图必须为其所有子视图分配大小和位置时调用。
onSizeChanged(int, int, int, int) 在此视图的大小发生更改时调用。
绘制 onDraw(Canvas) 在视图必须渲染其内容时调用。
事件处理 onKeyDown(int, KeyEvent) 在发生按键按下事件时调用。
onKeyUp(int, KeyEvent) 在发生 key up 事件时调用
onTrackballEvent(MotionEvent) 在发生轨迹球动作事件时调用。
onTouchEvent(MotionEvent) 在发生触摸屏动作事件时调用。
专注 onFocusChanged(boolean, int, Rect) 在视图获得或失去焦点时调用。
onWindowFocusChanged(boolean) 在包含视图的窗口获得或失去焦点时调用。
附加 onAttachedToWindow() 在视图附加到窗口时调用。
onDetachedFromWindow() 在视图与其窗口分离时调用。
onWindowVisibilityChanged(int) 在包含视图的窗口的可见性发生更改时调用。

复合控件

如果您不想创建完全自定义的组件,而是希望将 将可重复使用的组件组合在一起,该组件由一组现有控件组成,然后创建一个复合 组件(或复合控件)可能最合适。总而言之,这项变更整合了 原子控件或视图分解为可视为单个项的逻辑组。 例如,组合框可以是单行 EditText 字段的组合 以及一个附有弹出式列表的相邻按钮。如果用户点按按钮并从 列表,它会填充 EditText 字段,但用户也可以输入一些内容 直接导入到 EditText 中。

在 Android 中,还有另外两个视图可用于执行此操作:SpinnerAutoCompleteTextView。无论如何,这个组合框概念都是一个很好的例子。

如需创建复合组件,请执行以下操作:

  • Activity 一样,请使用声明式(基于 XML)方法 来创建包含的组件或通过编程方式从代码中嵌套它们。通过 通常从某种类型的 Layout 开始,因此请创建一个扩展 Layout。对于组合框,您可以使用 LinearLayout 和 横向。你可以在其中嵌套其他布局 任意复杂和结构化。
  • 在新类的构造函数中,接受父类所需的任何形参并传递 先传递到父类构造函数。然后,您可以设置其他视图 在新组件中您可以在这里创建 EditText 字段和 弹出式列表。您可以在 构造函数可以提取和使用它们。
  • (可选)为包含的视图可能生成的事件创建监听器。例如 listener 方法,以便更新列表项的点击监听器 如果选择了列表,则为 EditText
  • (可选)使用访问器和修饰符创建自己的属性。例如,让 EditText 值最初在组件中设置,并在出现以下情况时查询其内容: 所需的资源。
  • (可选)替换 onDraw()onMeasure()。在以下情况下,通常没有必要这样做: 扩展 Layout,因为布局具有可能正常运行的默认行为。
  • (可选)替换其他 on 方法(如 onKeyDown()),例如选择特定的 在点按某个键时,从组合框弹出式列表中获取默认值。

使用 Layout 作为自定义控件的基础有诸多优势, 包括:

  • 您可以使用声明式 XML 文件指定布局,就像使用 activity 屏幕一样, 或者,您可以通过编程方式创建视图,并通过代码将它们嵌套到布局中。
  • onDraw()onMeasure() 方法,以及大多数其他方法 on 方法具有合适的行为,因此您不必替换它们。
  • 您可以快速构建任意复杂的复合视图,并像使用它们一样重复使用它们。 单个组件。

修改现有视图类型

如果存在与您所需的组件类似的组件,您可以扩展该组件并替换 您想要更改的行为。借助完全定制的 Google Cloud 工具, 组件,但通过从 View 层次结构中更专用的类着手,您可以 从而实现免费执行您想要的行为。

例如, 记事本 示例应用演示了使用 Android 平台的多个方面。其中包括扩展了 EditText 视图可创建带线条的记事本。这并非完美的示例, 这样做可能会改变,但体现了原则。

如果您尚未执行此操作,请将记事本示例导入 Android Studio 或查看 来源。请特别留意 LinedEditText 的定义 在 NoteEditor.java 文件。

下面是此文件中的一些注意事项:

  1. 定义

    该类使用以下行进行定义:
    public static class LinedEditText extends EditText

    LinedEditText 定义为 NoteEditor 中的一个内部类 activity,但它是公共 activity,因此可以作为 NoteEditor.LinedEditText 进行访问 从 NoteEditor 类的外部进行调用。

    此外,LinedEditTextstatic,这意味着它不会生成 所谓的“合成方法”访问父类中的数据也就是说, 作为一个单独的类,而不是与 NoteEditor 密切相关的类。 如果内部类不需要从 输出。它使生成的类保持较小,并便于其他 类。

    LinedEditText 扩展了 EditText(即要在其中自定义的视图) 这种情况。完成后,新类可以代替常规 EditText 视图。

  2. 类初始化

    与往常一样,首先调用父类。这不是默认构造函数, 参数化一个。EditText 是使用以下参数创建的: 从 XML 布局文件扩充而来。因此,构造函数需要获取这些对象并将其传递给 父类构造函数。

  3. 替换的方法

    此示例仅替换 onDraw() 方法,但您可能需要替换 您可以自定义其他组件

    在此示例中,通过替换 onDraw() 方法,您可以在以下位置绘制蓝色线条: EditText 视图画布。画布会被传递到 onDraw() 方法结合使用。super.onDraw() 方法会在 方法结束。必须调用父类方法。在本例中,请在末尾 绘制要包含的线条。

  4. 自定义组件

    现在,您已经有了自定义组件,但如何使用它呢?在记事本示例中, 自定义组件直接从声明式布局中使用,因此请查看 note_editor.xmlres/layout 文件夹:

    <view xmlns:android="http://schemas.android.com/apk/res/android"
        class="com.example.android.notepad.NoteEditor$LinedEditText"
        android:id="@+id/note"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent"
        android:padding="5dp"
        android:scrollbars="vertical"
        android:fadingEdge="vertical"
        android:gravity="top"
        android:textSize="22sp"
        android:capitalize="sentences"
    />
    

    将自定义组件创建为 XML 中的通用视图,并将类指定为 使用完整的软件包。您定义的内部类可使用 NoteEditor$LinedEditText 表示法,这是引用内部 类。

    如果您的自定义视图组件未定义为内部类,您可以声明该视图 组件的名称,并排除 class 属性。例如:

    <com.example.android.notepad.LinedEditText
      id="@+id/note"
      ... />
    

    请注意,LinedEditText 类现在是一个单独的类文件。当 类嵌套在 NoteEditor 类中,则此方法不起作用。

    定义中的其他属性和参数是传递给自定义 然后传递到 EditText 构造函数。 它们与您用于 EditText 视图的参数相同。可以添加 您自己的参数。

您可以根据自己的需要创建自定义组件。

更复杂的组件可以替换更多的 on 方法,并引入其 自己的辅助方法,从而充分地自定义其属性和行为。唯一的限制是 以及您需要组件执行的操作。