Kotlin Android 基础知识 10.3:面向所有人进行设计

此 Codelab 是“Android Kotlin 基础知识”课程的一部分。如果您按顺序学习这些 Codelab,您将会充分发掘此课程的价值。“Android Kotlin 基础知识”Codelab 着陆页列出了所有课程 Codelab。

简介

无论您是出于兴趣还是商业目的进行开发,让应用尽可能多地被用户使用都是有意义的。实现这一目标需要考虑多个方面。

  • 支持从右向左 (RTL) 的语言。欧洲语言和许多其他语言都是从左向右阅读的,源自这些语言区域的应用通常设计得非常适合这些语言。许多其他语言(例如阿拉伯语)的书写方向为从右到左,这些语言的母语使用者数量众多。让您的应用支持从右到左 (RTL) 语言,以扩大潜在受众群体。
  • 扫描无障碍功能问题。猜测他人可能会如何体验您的应用是一种存在陷阱的选择。 无障碍功能扫描仪应用可免去猜测环节,分析您的应用,找出可改进其无障碍功能的地方。
  • 以内容说明为 TalkBack 设计。视力障碍比人们想象的更为常见,许多用户(不仅是盲人)都会使用屏幕阅读器。内容说明是指当用户与屏幕上的元素互动时,屏幕阅读器要朗读的短语。
  • 支持夜间模式。对于许多有视觉障碍的用户来说,更改屏幕颜色可以提高对比度,帮助他们直观地使用您的应用。Android 可让您轻松支持夜间模式,您应始终支持夜间模式,以便为用户提供默认屏幕颜色的简单替代方案。

在此 Codelab 中,您将探索这些选项中的每一个,并为 GDG Finder 应用添加对这些选项的支持。

您还将学习如何在 Android 应用中使用微件。您可以使用微件让应用更具趣味性,同时保持应用的无障碍性。

您应当已掌握的内容

您应熟悉以下内容:

  • 如何创建包含 activity 和 fragment 的应用,以及如何在 fragment 之间导航并传递数据。
  • 使用视图和视图组来布局界面,尤其是 RecyclerView。
  • 如何将架构组件(包括 ViewModel)与推荐的架构搭配使用,以创建结构合理且高效的应用。
  • 数据绑定、协程以及如何处理鼠标点击。
  • 如何连接到互联网并使用 Room 数据库在本地缓存数据。
  • 如何设置视图属性,以及如何将资源提取到 XML 资源文件中并使用其中的资源。
  • 如何使用样式和主题来自定义应用的外观。
  • 如何使用 Material 组件、维度资源和自定义着色。

学习内容

  • 如何让应用可供尽可能多的用户使用。
  • 如何让应用支持从右到左 (RTL) 书写的语言。
  • 如何评估应用的无障碍功能。
  • 如何使用内容说明让应用更好地与屏幕阅读器搭配使用。
  • 如何使用 chip。
  • 如何让应用支持深色模式。

您将执行的操作

  • 评估并扩展给定应用,使其支持 RTL 语言,从而提高无障碍功能。
  • 扫描应用,确定哪些方面可以改进无障碍功能。
  • 为图片使用内容说明。
  • 了解如何使用可绘制对象。
  • 为应用添加使用夜间模式的功能。

GDG-finder 初始应用基于您在本课程中目前为止所学的所有知识构建而成。

该应用使用 ConstraintLayout 来布局三个界面。其中两个屏幕只是布局文件,您将使用它们来探索 Android 上的颜色和文本。

第三个界面是 GDG 查找器。GDG(Google 开发者社区)是由开发者组成的社区,专注于 Google 技术,包括 Android。世界各地的 GDG 都会举办聚会、会议、学习聚会和其他活动。

在开发此应用时,您需要处理真实的 GDG 列表。查找器界面会使用设备的位置信息按距离对 GDG 进行排序。

如果您很幸运,您所在的地区有 GDG,您可以访问其网站并报名参加其活动!GDG 活动是结识其他 Android 开发者并学习本课程未涵盖的行业最佳实践的好方法。

以下屏幕截图展示了您的应用在此 Codelab 的开始和结束时会发生哪些变化。

从左到右 (LTR) 语言和从右到左 (RTL) 语言之间的主要区别在于显示内容的阅读方向。当界面方向从 LTR 更改为 RTL(或反之)时,通常称为镜像。镜像会影响屏幕上的大部分内容,包括文字、文本字段图标、布局和带有方向的图标(例如箭头)。其他内容不会镜像,例如数字(时钟、电话号码)、没有方向的图标(飞行模式、WiFi)、播放控件以及大多数图表。

全球有超过 10 亿人使用 RTL 文字方向的语言。Android 开发者遍布全球,因此 GDG Finder 应用需要支持 RTL 语言。

第 1 步:添加 RTL 支持

在此步骤中,您将使 GDG Finder 应用支持 RTL 语言。

  1. 下载并运行 GDGFinderMaterial 应用(此 Codelab 的起始应用),或者继续使用上一个 Codelab 的最终代码。
  2. 打开 Android 清单。
  3. <application> 部分,添加以下代码以指定应用支持 RTL。
<application
        ...
        android:supportsRtl="true">
  1. Design 标签页中打开 activity_main.xml
  2. 预览的语言区域下拉菜单中,选择从右向左预览。(如果您找不到此菜单,请展开窗格或关闭属性窗格以显示该菜单。)

  1. 预览中,您会发现标题“GDG Finder”已移至右侧,而屏幕的其余部分基本保持不变。总的来说,此界面还算过得去。但文本视图中的对齐方式现在是错误的,因为它是向左对齐,而不是向右对齐。

  1. 如需在设备上实现此功能,请在设备或模拟器的设置中,在开发者选项中选择强制使用从右到左的布局。(如果您需要开启开发者选项,请找到版本号并点击它,直到您收到一条提示您已成为开发者的消息。具体因设备和 Android 系统版本而异。)

  1. 运行应用,并在设备上验证主屏幕是否与预览中的相同。请注意,FAB 现在已切换到左侧,而汉堡菜单已切换到右侧!
  2. 在应用中,打开抽屉式导航栏,然后前往搜索界面。如下所示,图标仍位于左侧,但看不到任何文字。结果发现,文字位于屏幕外,在图标的左侧。这是因为代码在视图属性和布局限制中使用了左侧/右侧屏幕引用。

第 2 步:使用 start 和 end 而不是 left 和 right

屏幕上的“左”和“右”(当您面向屏幕时)不会发生变化,即使文字的方向发生变化也是如此。例如,layout_constraintLeft_toLeftOf 始终将元素的左侧约束到屏幕的左侧。在您的应用中,RTL 语言的文字会超出屏幕范围,如上方的屏幕截图所示。

若要解决此问题,请使用 StartEnd 术语,而不是“左”和“右”。此术语会根据当前语言的文本方向,相应地设置文本的开头和结尾,以便边距和布局位于屏幕的正确区域。

  1. Open list_item.xml
  2. 将对 LeftRight 的所有引用替换为对 StartEnd 的引用。
app:layout_constraintStart_toStartOf="parent"

app:layout_constraintStart_toEndOf="@+id/gdg_image"
app:layout_constraintEnd_toEndOf="parent"
  1. ImageViewlayout_marginLeft 替换为 layout_marginStart。这样可将边距移至正确位置,使图标远离屏幕边缘。
<ImageView
android:layout_marginStart="
?
  1. 打开 fragment_gdg_list.xml。在预览窗格中查看 GDG 列表。请注意,由于图标被镜像,因此仍指向错误的方向(如果图标未被镜像,请确保您仍在查看从右到左的预览)。根据 Material Design 指南,图标不应镜像。
  2. 打开 res/drawable/ic_gdg.xml
  3. 在 XML 代码的第一行中,找到并删除 android:autoMirrored="true" 以停用镜像。
  4. 查看预览版,或再次运行该应用并打开“搜索 GDG”界面。布局现在应该已修复!

第 3 步:让 Android Studio 为您代劳

在上一练习中,您迈出了支持 RTL 语言的第一步。幸运的是,Android Studio 可以扫描您的应用并为您设置许多基本内容。

  1. list_item.xmlTextView 中,将 layout_marginStart 改回 layout_marginLeft,以便扫描器可以找到内容。
<TextView
android:layout_marginLeft="@dimen/spacing_normal"
  1. 在 Android Studio 中,依次选择 Refactor > Add RTL support where possible,然后选中用于更新清单和布局文件的复选框,以使用 start 和 end 属性。

  1. 重构预览窗格中,找到 app 文件夹,然后展开该文件夹,直到显示所有详细信息。
  2. 在应用文件夹下,您会看到刚刚更改的 layout_marginLeft 列为要重构的代码。

  1. 请注意,预览中还会列出系统文件和库文件。右键点击 layout 和 layout-watch-v20 以及不属于 app 的任何其他文件夹,然后从上下文菜单中选择 Exclude

  1. 现在,继续进行重构。(如果您收到有关系统文件的弹出式窗口,请确保您已排除所有不属于应用代码的文件夹。)
  1. 请注意,layout_marginLeft 已更改回 layout_marginStart

第 3 步:探索语言区域文件夹

到目前为止,您只是更改了应用所用默认语言的方向。对于正式版应用,您需要将 strings.xml 文件发送给翻译人员,让其翻译成新语言。在此 Codelab 中,应用提供了一个西班牙语 strings.xml 文件(我们使用 Google 翻译生成了翻译,因此翻译并不完美)。

  1. 在 Android Studio 中,将项目视图切换为项目文件
  2. 展开 res 文件夹,然后注意 res/valuesres/values-es 文件夹。文件夹名称中的“es”是西班牙语的语言代码values-"语言代码"文件夹包含每种受支持语言的值。不带扩展名的 values 文件夹包含在其他情况下适用的默认资源。

  1. values-es 中,打开 strings.xml,您会发现所有字符串都是西班牙语。
  2. 在 Android Studio 中,在 Design 标签页中打开 activity_main.xml
  3. 预览的语言区域下拉菜单中,选择西班牙语。您的文字现在应该会以西班牙语显示。

  1. [可选] 如果您精通 RTL 语言,请创建 values 文件夹和相应语言的 strings.xml,并测试其在设备上的显示效果。
  2. [可选] 更改设备上的语言设置,然后运行应用。请务必不要将设备更改为自己不熟悉的语言,否则撤消更改会比较困难!

在上一个任务中,您手动更改了应用,然后使用 Android Studio 检查是否有其他需要进行的 RTL 改进。

在使应用可供访问方面,无障碍功能扫描仪应用是您的最佳帮手。它会在目标设备上扫描您的应用,并建议您进行改进,例如放大触控目标、增加对比度以及为图片提供说明,从而让您的应用更易于访问。无障碍功能扫描仪由 Google 制作,您可以从 Play 商店安装它。

第 1 步:安装并运行无障碍功能扫描仪

  1. 打开 Play 商店,并根据需要登录。您可以在实体设备或模拟器上执行此操作。此 Codelab 使用模拟器。
  1. 在 Play 商店中,搜索 Google LLC 的无障碍功能扫描仪。请务必获取 Google 发布的正确应用,因为任何扫描都需要大量权限!

  1. 在模拟器上安装扫描仪。
  2. 安装完成后,点击打开
  3. 点击开始使用
  4. 点击确定,在“设置”中开始设置无障碍功能扫描仪。

  1. 点击无障碍功能扫描仪,前往设备的无障碍设置。

  1. 点击使用服务即可启用该服务。

  1. 按照屏幕上的说明操作,并授予所有权限。
  2. 然后,点击好的,知道了,返回到主屏幕。您可能会在屏幕上的某个位置看到一个带有对勾标记的蓝色按钮。点击此按钮会触发对前台应用的测试。您可以拖动该按钮来调整其位置。此按钮会始终显示在任何应用之上,因此您可以随时触发测试。

  1. 打开或运行您的应用。
  2. 点击蓝色按钮,并接受其他安全警告和权限。

首次点击无障碍扫描器图标时,该应用会请求获取屏幕上显示的所有内容的权限。这似乎是一项非常可怕的权限,事实也确实如此。

您几乎绝不应授予此类权限,因为该权限允许应用读取您的电子邮件,甚至获取您的银行账号信息!不过,为了让无障碍扫描器正常运行,它需要像用户一样检查您的应用,因此需要此权限。

  1. 点击蓝色按钮,然后等待分析完成。您会看到类似以下屏幕截图的内容,其中标题和 FAB 用红色框标出。这表示此界面上有两条无障碍功能改进建议。

  1. 点击 GDG 查找器周围的方框。系统随即会打开一个包含其他信息的窗格,如下所示,其中指出了图片对比度存在问题。
  2. 展开 图片对比度信息,该工具会建议补救措施。
  3. 点击右侧的箭头可查看下一项的信息。

  1. 在您的应用中,前往 Apply for GDG 界面,然后使用无障碍功能扫描仪应用扫描该界面。系统会提供许多建议,如下方左侧所示。确切地说,是 12 个。公平地说,其中一些是类似商品的重复评价。
  2. 点击底部工具栏中的“堆叠”图标 ,即可获取所有建议的列表,如右侧屏幕截图所示。您将在本 Codelab 中解决所有这些问题。

Android 无障碍套件是 Google 推出的一系列应用,其中包含可帮助提高应用无障碍程度的工具。它包括 TalkBack 等工具。TalkBack 是一款屏幕阅读器,可提供听觉、触觉和语音反馈,让用户无需使用双眼即可在设备上浏览和使用内容。

事实证明,TalkBack 不仅供盲人使用,还供许多有某种视力障碍的人使用。甚至只是想让眼睛休息一下的人!

因此,无障碍功能适合所有人!在此任务中,您将试用 TalkBack 并更新应用,使其能够与 TalkBack 良好搭配使用。

第 1 步:安装并运行无障碍套件

TalkBack 预安装在许多实体设备上,但在模拟器上,您需要安装它。

  1. 打开 Play 商店。
  2. 找到无障碍套件。确保该应用是 Google 的正确应用。
  3. 如果未安装,请安装无障碍服务套装。
  4. 如需在设备上启用 TalkBack,请依次前往设置 > 无障碍功能,然后选择使用服务,开启 TalkBack。与无障碍扫描器一样,TalkBack 需要获得权限才能读取屏幕上的内容。接受权限请求后,TalkBack 会显示一个教程列表,教您如何有效使用 TalkBack。
  5. 请在此处暂停,并学习教程,即使只是为了了解如何在完成操作后重新关闭 TalkBack。
  6. 如需退出教程,请点击返回按钮以选择它,然后点按两次屏幕上的任意位置。
  1. 使用 TalkBack 探索 GDG Finder 应用。注意 TalkBack 未提供有关屏幕或控件的实用信息的地方。您将在下一个练习中修复此问题。

第 2 步:添加内容说明

内容描述标签是用于说明视图含义的描述性标签。大多数视图都应包含内容说明。

  1. 在 GDG Finder 应用运行且 TalkBack 已启用的情况下,前往申请运行 GDG 屏幕。
  2. 点按主图片,但没有任何反应。
  3. 打开 add_gdg_fragment.xml
  4. ImageView 中,添加内容描述符属性,如下所示。strings.xml 中为您提供了 stage_image_description 字符串。
android:contentDescription="@string/stage_image_description"
  1. 运行应用。
  2. 前往申请运行 GDG,然后点击相应图片。现在,您应该会听到该图片的简短说明。
  3. [可选] 为此应用中的其他图片添加内容说明。在正式版应用中,所有图片都需要有内容说明。

第 3 步:向可修改的文本字段添加提示

对于可编辑的元素(例如 EditText),您可以在 XML 中使用 android:hint 来帮助用户了解要输入的内容。提示始终在界面中显示,因为它是输入字段中的默认文本。

  1. 仍在 add_gdg_fragment.xml 中。
  2. 添加内容说明和提示,并使用以下代码作为指导。

添加到 textViewIntro

android:contentDescription="@string/add_gdg"

分别添加到编辑文本中:

android:hint="@string/your_name_label"

android:hint="@string/email_label"

android:hint="@string/city_label"

android:hint="@string/country_label"

android:hint="@string/region_label"
  1. labelTextWhy 添加内容说明。
android:contentDescription="@string/motivation" 
  1. EditTextWhy 添加提示。为编辑框添加标签后,请为标签添加内容说明,并为该框添加提示。
android:hint="@string/enter_motivation"
  1. 为提交按钮添加内容说明。所有按钮都需要提供说明,说明按下按钮后会发生什么。
android:contentDescription="@string/submit_button_description"
  1. 在启用 TalkBack 的情况下运行应用,然后填写表单以申请运行 GDG。

第 4 步:创建内容组

对于 TalkBack 应视为一组的界面控件,您可以使用内容分组。分组在一起的相关内容会一起公布。这样一来,辅助技术的用户就不需要经常滑动、扫描或等待,即可发现屏幕上的所有信息。这不会影响控件在屏幕上的显示方式。

如需对界面组件进行分组,请将其封装到 ViewGroup(例如 LinearLayout)中。在 GDG Finder 应用中,labelTextWhyeditTextWhy 元素在语义上属于同一组,因此非常适合分组。

  1. 打开 add_gdg_fragment.xml
  2. LinearLayout 环绕在 LabelTextWhyEditTextWhy 周围,以创建内容组。复制并粘贴以下代码。此 LinearLayout 已包含您所需的部分样式。(请确保 button 位于 LinearLayout 之外。)
<LinearLayout android:id="@+id/contentGroup" android:layout_width="match_parent"
            android:layout_height="wrap_content" android:focusable="true"
            app:layout_constraintTop_toBottomOf="@id/EditTextRegion"
            android:orientation="vertical" app:layout_constraintStart_toStartOf="@+id/EditTextRegion"
            app:layout_constraintEnd_toEndOf="@+id/EditTextRegion"
            android:layout_marginTop="16dp" app:layout_constraintBottom_toTopOf="@+id/button"
            android:layout_marginBottom="8dp">

     <!-- label and edit text here –>

<LinearLayout/>
  1. 依次选择 Code > Reformat code,以正确缩进所有代码。
  2. labelTextWhyeditTextWhy 中移除所有布局边距。
  3. labelTextWhy 中,将 layout_constraintTop_toTopOf 约束条件更改为 contentGroup
app:layout_constraintTop_toTopOf="@+id/contentGroup" />
  1. editTextWhy 中,将 layout_constraintBottom_toBottomOf 约束更改为 contentGroup
app:layout_constraintBottom_toBottomOf="@+id/contentGroup"
  1. EditTextRegionButton 约束到 contentGroup,以消除错误。
app:layout_constraintBottom_toTopOf="@+id/contentGroup"
  1. LinearLayout 添加边距。(可选)将此边际提取为维度。
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"

如果您需要帮助,请对照解决方案代码中的 add_gdg_fragment.xml 检查您的代码。

  1. 运行应用,并使用 TalkBack 浏览申请运行 GDG 界面。

第 5 步:添加实时区域

目前,提交按钮上的标签为确定。最好是,在提交表单之前,按钮有一个标签和说明;在用户点击并提交表单后,动态更改为其他标签和内容说明。您可以使用实时区域来实现此目的。

动态区域用于向无障碍服务表明,当视图发生变化时是否应通知用户。例如,告知用户密码不正确或出现网络错误是提高应用无障碍性的好方法。在此示例中,为简单起见,您会在提交按钮更改其状态时通知用户。

  1. 打开 add_gdg_fragment.xml
  2. 使用提供的 submit 字符串资源将按钮的文本分配更改为 Submit
android:text="@string/submit"
  1. 通过设置 android:accessibilityLiveRegion 属性,为按钮添加实时区域。在您输入时,系统会提供多个值选项。您可以根据更改的重要性选择是否中断用户。如果值为“assertive”,无障碍服务会中断正在进行的语音,立即宣布此视图的更改。如果您将该值设置为“none”,则不会公布任何更改。设置为“礼貌”时,无障碍服务会宣布更改,但会等待轮到自己。将值设置为“polite”。

android:accessibilityLiveRegion="polite"
  1. add 软件包中,打开 AddGdgFragment.kt
  2. showSnackBarEvent Observer 中,在您完成 SnackBar 的显示后,为按钮设置新的内容说明和文本。
binding.button.contentDescription=getString(R.string.submitted)
binding.button.text=getString(R.string.done)
  1. 运行应用,然后点击按钮。很遗憾,按钮和字体太小了!

第 6 步:修复按钮样式

  1. add_gdg_fragment.xml 中,将按钮的 widthheight 更改为 wrap_content,以便显示完整标签,并使按钮大小合适。
android:layout_width="wrap_content"
android:layout_height="wrap_content"
  1. 从按钮中删除 backgroundTinttextColortextSize 属性,以便应用使用更好的主题样式。
  2. textViewIntro 中删除 textColor 属性。主题颜色应提供良好的对比度。
  3. 运行应用。请注意,提交按钮的可用性大大提高。点击提交,并注意它如何变为完成

信息块是表示属性、文本、实体或操作的紧凑元素。用户可以通过这些组件输入信息、选择选项、过滤内容或触发操作。

Chip widget 是 ChipDrawable 周围的精简视图封装容器,其中包含所有布局和绘制逻辑。添加了额外的逻辑来支持触控、鼠标、键盘和无障碍导航。主功能块和关闭图标被视为单独的逻辑子视图,包含各自的导航行为和状态。

条状标签使用可绘制对象。借助 Android 可绘制对象,您可以在屏幕上绘制图片、形状和动画,这些对象可以具有固定大小,也可以动态调整大小。您可以使用图片作为可绘制对象,例如 GDG 应用中的图片。您还可以使用矢量绘图来绘制任何您能想到的内容。还有一种可调整大小的可绘制对象,称为 9-patch 可绘制对象,此 Codelab 中未介绍。drawable/ic_gdg.xml 中的 GDG 徽标是另一个可绘制对象。

可绘制对象不是视图,因此您无法将可绘制对象直接放在 ConstraintLayout 内,而需要将其放在 ImageView 内。您还可以使用可绘制对象为文本视图或按钮提供背景,并且背景会绘制在文本后面。

第 1 步:向 GDG 列表添加 chip

以下已勾选的 chip 使用了三个可绘制对象。背景和对勾标记都是可绘制对象。触摸微芯片会产生波纹效果,这是通过特殊的 RippleDrawable 实现的,该对象会根据状态变化显示波纹效果。

在此任务中,您将向 GDG 列表添加 chip,并让它们在被选中时更改状态。在此练习中,您将在搜索屏幕顶部添加一行称为微件的按钮。每个按钮都会过滤 GDG 列表,以便用户仅收到所选区域的结果。选择按钮后,按钮会更改其背景并显示对勾标记。

  1. 打开 fragment_gdg_list.xml
  2. HorizontalScrollView. 中创建一个 com.google.android.material.chip.ChipGroup 将其 singleLine 属性设置为 true,以便所有 chip 都排列在一条可水平滚动的线上。将 singleSelection 属性设置为 true,以便一次只能选择组中的一个 chip。代码如下。
<com.google.android.material.chip.ChipGroup
    android:id="@+id/region_list"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:singleSelection="true"
    android:padding="@dimen/spacing_normal"/>
  1. layout 文件夹中,创建一个名为 region.xml 的新布局资源文件,用于定义一个 Chip 的布局。
  2. 在 region.xml 中,将所有代码替换为以下适用于一个 Chip 的布局。请注意,此 Chip 是一个 Material 组件。另请注意,您可以通过设置属性 app:checkedIconVisible 来获得对勾标记。您会因缺少 selected_highlight 颜色而收到错误。
<?xml version="1.0" encoding="utf-8"?>

<com.google.android.material.chip.Chip
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        style="@style/Widget.MaterialComponents.Chip.Choice"
        app:chipBackgroundColor="@color/selected_highlight"
        app:checkedIconVisible="true"
        tools:checked="true"/>
  1. 如需创建缺少的 selected_highlight 颜色,请将光标放在 selected_highlight 上,调出意图菜单,然后为所选突出显示创建颜色资源。默认选项没有问题,因此只需点击确定即可。系统会在 res/color 文件夹中创建该文件。
  2. 打开 res/color/selected_highlight.xml。在此颜色状态列表(编码为 <selector>)中,您可以为不同的状态提供不同的颜色。每种状态和关联的颜色都编码为 <item>。如需详细了解这些颜色,请参阅颜色主题
  1. <selector> 内,向状态列表添加一个具有默认颜色 colorOnSurface 的项。在状态列表中,务必要始终涵盖所有状态。一种方法是设置默认颜色。
<item android:alpha="0.18" android:color="?attr/colorOnSurface"/>
  1. 在默认颜色上方,添加一个颜色为 colorPrimaryVariantitem,并将其使用限制为仅在所选状态为 true 时使用。系统会从上到下处理状态列表,就像处理 case 语句一样。如果没有任何状态匹配,则应用默认状态。
<item android:color="?attr/colorPrimaryVariant"
         android:state_selected="true" />

第 2 步:显示 chip 行

GDG 应用会创建一个显示有 GDG 的地区的 chip 列表。选择某个功能块后,应用会过滤结果,仅显示该区域的 GDG 结果。

  1. search 软件包中,打开 GdgListFragment.kt
  2. onCreateView() 中,在 return 语句的正上方,添加 viewModel.regionList 的观察器并替换 onChanged()。当视图模型提供的地区列表发生变化时,需要重新创建 chip。添加一条语句,以便在提供的 datanull 时立即返回。
viewModel.regionList.observe(viewLifecycleOwner, object: Observer<List<String>> {
        override fun onChanged(data: List<String>?) {
             data ?: return
        }
})
  1. onChanged() 内,在 null 测试下方,将 binding.regionList 分配给一个名为 chipGroup 的新变量,以缓存 regionList
val chipGroup = binding.regionList
  1. 在下方,创建一个新的 layoutInflator,用于从 chipGroup.context 中扩充 chip。
val inflator = LayoutInflater.from(chipGroup.context)
  1. 清理并重建项目,以消除数据绑定错误。

在膨胀器下方,您现在可以创建实际的 chip,regionList 中的每个区域对应一个 chip。

  1. 创建一个变量 children,用于存放所有 chip。为其分配传入的 data 上的映射函数,以创建并返回每个 chip。
val children = data.map {} 
  1. 在 map lambda 中,为每个 regionName 创建并扩充一个 chip。完整代码如下所示。
val children = data.map {
   val children = data.map { regionName ->
       val chip = inflator.inflate(R.layout.region, chipGroup, false) as Chip
       chip.text = regionName
       chip.tag = regionName
       // TODO: Click listener goes here.
       chip
   }
}
  1. 在 lambda 中,在返回 chip 之前,添加一个点击监听器。当点击 chip 时,将其状态设置为 checked。在 viewModel 中调用 onFilterChanged(),这会触发一系列事件,以获取相应过滤条件的结果。
chip.setOnCheckedChangeListener { button, isChecked ->
   viewModel.onFilterChanged(button.tag as String, isChecked)
}
  1. 在 lambda 的末尾,从 chipGroup 中移除所有当前视图,然后将 children 中的所有条状标签添加到 chipGroup。(您无法更新功能块,因此必须移除并重新创建 chipGroup 的内容。)
chipGroup.removeAllViews()

for (chip in children) {
   chipGroup.addView(chip)
}

完成后的观察者应如下所示:

   override fun onChanged(data: List<String>?) {
       data ?: return

       val chipGroup = binding.regionList
       val inflator = LayoutInflater.from(chipGroup.context)

       val children = data.map { regionName ->
           val chip = inflator.inflate(R.layout.region, chipGroup, false) as Chip
           chip.text = regionName
           chip.tag = regionName
           chip.setOnCheckedChangeListener { button, isChecked ->
               viewModel.onFilterChanged(button.tag as String, isChecked)
           }
           chip
       }
       chipGroup.removeAllViews()

       for (chip in children) {
           chipGroup.addView(chip)
       }
   }
})
  1. 运行应用并搜索 GDGS,打开搜索界面,以使用新芯片。点击每个功能块后,应用会在下方显示过滤条件组。

夜间模式可让应用将其颜色更改为深色主题,例如,当设备设置已设置为启用夜间模式时。在夜间模式下,应用会将其默认的浅色背景更改为深色,并相应地更改所有其他屏幕元素。

第 1 步:启用夜间模式

如需为应用提供深色主题,请将其主题从 Light 主题更改为名为 DayNight 的主题。DayNight 主题会根据模式显示为浅色或深色。

  1. styles.xml, 中,将 AppTheme 父主题从 Light 更改为 DayNight
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
  1. MainActivityonCreate() 方法中,调用 AppCompatDelegate.setDefaultNightMode() 以通过编程方式开启深色主题。
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
  1. 运行应用,并验证应用是否已切换到深色主题。

第 2 步:生成您自己的深色主题调色板

如需自定义深色主题,请创建带有 -night 限定符的文件夹,以供深色主题使用。例如,您可以创建一个名为 values-night 的文件夹,以便在夜间模式下使用特定颜色。

  1. 访问 material.io 颜色选择器工具,然后创建夜间主题调色板。例如,您可以基于深蓝色。
  2. 生成并下载 colors.xml 文件。
  3. 切换到 Project Files 视图,以列出项目中的所有文件夹。
  4. 找到 res 文件夹并将其展开。
  5. 创建 res/values-night 资源文件夹。
  6. 将新的 colors.xml 文件添加到 res/values-night 资源文件夹。
  7. 运行您的应用(仍启用夜间模式),应用应会使用您为 res/values-night 定义的新颜色。请注意,条状标签使用了新的辅助颜色。

Android Studio 项目:GDGFinderFinal

支持从右向左书写的语言

  • 在 Android 清单中,设置 android:supportsRtl="true"
  • 您可以在模拟器中预览 RTL,并使用自己的语言检查屏幕布局。在设备或模拟器上,打开设置,然后在开发者选项中选择强制使用从右到左的布局。
  • 将对 LeftRight 的引用替换为对 StartEnd 的引用。
  • 通过删除 android:autoMirrored="true" 停用可绘制对象的镜像。
  • 选择 Refactor > Add RTL support where possible ,让 Android Studio 为您完成这项工作。
  • 使用 values-"语言代码" 文件夹存储特定于语言的资源。

扫描无障碍功能问题

以内容说明为设计宗旨,打造 TalkBack 体验

  • 安装 Google 提供的 Android 无障碍套件,其中包含 TalkBack。
  • 为所有界面元素添加内容说明。例如:
    android:contentDescription="@string/stage_image_description"
  • 对于 EditText 等可编辑元素,请在 XML 中使用 android:hint 属性,向用户提供有关输入内容的提示。
  • 通过将相关元素封装到视图组中来创建内容组。
  • 创建实时区域,以便使用 android:accessibilityLiveRegion 为用户提供更多反馈。

使用条状标签实现过滤功能

  • 信息块是表示属性、文本、实体或操作的精简元素。
  • 如需创建芯片组,请使用 com.google.android.material.chip.ChipGroup
  • 定义一个 com.google.android.material.chip.Chip 的布局。
  • 如果您希望芯片更改颜色,请提供一个颜色状态列表作为具有状态颜色值的 <selector>
    <item android:color="?attr/colorPrimaryVariant"
    android:state_selected="true" />
  • 通过向视图模型中的数据添加观察者,将条状标签绑定到实时数据。
  • 如需显示 chip,请为 chip 组创建充气器:
    LayoutInflater.from(chipGroup.context)
  • 创建 chip,添加用于触发所需操作的点击监听器,并将 chip 添加到 chip 组。

支持深色模式

  • 使用 DayNight AppTheme 支持深色模式。
  • 您可以通过编程方式设置深色模式:
    AppCompatDelegate.setDefaultNightMode()
  • 创建 res/values-night 资源文件夹,以提供深色模式的自定义颜色和值。

Android 开发者文档:

其他资源:

此部分列出了在由讲师主导的课程中,学生学习此 Codelab 后可能需要完成的家庭作业。讲师自行决定是否执行以下操作:

  • 根据需要布置作业。
  • 告知学生如何提交家庭作业。
  • 给家庭作业评分。

讲师可以酌情采纳这些建议,并且可以自由布置自己认为合适的任何其他家庭作业。

如果您是在自学此 Codelab,可随时通过这些家庭作业来检测您的知识掌握情况。

问题 1

要支持 RTL 语言,以下执行以下哪项操作?

▢ 将属性中的 LeftRight 替换为 StartEnd

▢ 切换为 RTL 语言

▢ 确保所有图标均使用 android:autoMirrored="true"

▢ 提供内容说明

问题 2

大多数 Android 设备上都内置了以下哪些无障碍工具?

▢ TalkBack

▢ 无障碍功能扫描仪

▢ 在 Android Studio 中,依次选择 Refactor > Add RTL support where possible

▢ Lint

问题 3

以下关于 chip 的表述中,哪一项是不正确的?

▢ 您将芯片显示为 ChipGroup 的一部分。

▢ 您可以为 ChipGroup 提供颜色状态列表。

▢ 信息块是表示输入、属性或操作的紧凑元素。

▢ 如果您的应用使用 chip,则必须始终启用 DarkTheme

问题 4

哪个主题可提供深色和浅色模式的样式?

DayNight

DarkTheme

DarkAndLightTheme

Light

问题 5

什么是动态区域?

▢ 包含对用户非常重要的信息的节点

▢ 根据 Material 准则改变形状的屏幕区域

▢ 允许流式传输视频的视图

▢ 带动画的可绘制对象