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

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

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

- 运行应用,并在设备上验证主屏幕是否与预览中的相同。请注意,FAB 现在已切换到左侧,而汉堡菜单已切换到右侧!
- 在应用中,打开抽屉式导航栏,然后前往搜索界面。如下所示,图标仍位于左侧,但看不到任何文字。结果发现,文字位于屏幕外,在图标的左侧。这是因为代码在视图属性和布局限制中使用了左侧/右侧屏幕引用。
|
|
第 2 步:使用 start 和 end 而不是 left 和 right
屏幕上的“左”和“右”(当您面向屏幕时)不会发生变化,即使文字的方向发生变化也是如此。例如,layout_constraintLeft_toLeftOf 始终将元素的左侧约束到屏幕的左侧。在您的应用中,RTL 语言的文字会超出屏幕范围,如上方的屏幕截图所示。
若要解决此问题,请使用 Start 和 End 术语,而不是“左”和“右”。此术语会根据当前语言的文本方向,相应地设置文本的开头和结尾,以便边距和布局位于屏幕的正确区域。
Openlist_item.xml。- 将对
Left和Right的所有引用替换为对Start和End的引用。
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@+id/gdg_image"
app:layout_constraintEnd_toEndOf="parent"- 将
ImageView的layout_marginLeft替换为layout_marginStart。这样可将边距移至正确位置,使图标远离屏幕边缘。
<ImageView
android:layout_marginStart="
?- 打开
fragment_gdg_list.xml。在预览窗格中查看 GDG 列表。请注意,由于图标被镜像,因此仍指向错误的方向(如果图标未被镜像,请确保您仍在查看从右到左的预览)。根据 Material Design 指南,图标不应镜像。 - 打开 res/drawable/ic_gdg.xml。
- 在 XML 代码的第一行中,找到并删除
android:autoMirrored="true"以停用镜像。 - 查看预览版,或再次运行该应用并打开“搜索 GDG”界面。布局现在应该已修复!

第 3 步:让 Android Studio 为您代劳
在上一练习中,您迈出了支持 RTL 语言的第一步。幸运的是,Android Studio 可以扫描您的应用并为您设置许多基本内容。
- 在 list_item.xml 的
TextView中,将layout_marginStart改回layout_marginLeft,以便扫描器可以找到内容。
<TextView
android:layout_marginLeft="@dimen/spacing_normal"- 在 Android Studio 中,依次选择 Refactor > Add RTL support where possible,然后选中用于更新清单和布局文件的复选框,以使用 start 和 end 属性。

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

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

- 现在,继续进行重构。(如果您收到有关系统文件的弹出式窗口,请确保您已排除所有不属于应用代码的文件夹。)
- 请注意,
layout_marginLeft已更改回layout_marginStart。
第 3 步:探索语言区域文件夹
到目前为止,您只是更改了应用所用默认语言的方向。对于正式版应用,您需要将 strings.xml 文件发送给翻译人员,让其翻译成新语言。在此 Codelab 中,应用提供了一个西班牙语 strings.xml 文件(我们使用 Google 翻译生成了翻译,因此翻译并不完美)。
- 在 Android Studio 中,将项目视图切换为项目文件。
- 展开 res 文件夹,然后注意 res/values 和 res/values-es 文件夹。文件夹名称中的“es”是西班牙语的语言代码。values-"语言代码"文件夹包含每种受支持语言的值。不带扩展名的 values 文件夹包含在其他情况下适用的默认资源。

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

- [可选] 如果您精通 RTL 语言,请创建 values 文件夹和相应语言的 strings.xml,并测试其在设备上的显示效果。
- [可选] 更改设备上的语言设置,然后运行应用。请务必不要将设备更改为自己不熟悉的语言,否则撤消更改会比较困难!
在上一个任务中,您手动更改了应用,然后使用 Android Studio 检查是否有其他需要进行的 RTL 改进。
在使应用可供访问方面,无障碍功能扫描仪应用是您的最佳帮手。它会在目标设备上扫描您的应用,并建议您进行改进,例如放大触控目标、增加对比度以及为图片提供说明,从而让您的应用更易于访问。无障碍功能扫描仪由 Google 制作,您可以从 Play 商店安装它。
第 1 步:安装并运行无障碍功能扫描仪
- 打开 Play 商店,并根据需要登录。您可以在实体设备或模拟器上执行此操作。此 Codelab 使用模拟器。
- 在 Play 商店中,搜索 Google LLC 的无障碍功能扫描仪。请务必获取 Google 发布的正确应用,因为任何扫描都需要大量权限!

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

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

- 点击使用服务即可启用该服务。

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

- 打开或运行您的应用。
- 点击蓝色按钮,并接受其他安全警告和权限。
首次点击无障碍扫描器图标时,该应用会请求获取屏幕上显示的所有内容的权限。这似乎是一项非常可怕的权限,事实也确实如此。
您几乎绝不应授予此类权限,因为该权限允许应用读取您的电子邮件,甚至获取您的银行账号信息!不过,为了让无障碍扫描器正常运行,它需要像用户一样检查您的应用,因此需要此权限。
- 点击蓝色按钮,然后等待分析完成。您会看到类似以下屏幕截图的内容,其中标题和 FAB 用红色框标出。这表示此界面上有两条无障碍功能改进建议。

- 点击 GDG 查找器周围的方框。系统随即会打开一个包含其他信息的窗格,如下所示,其中指出了图片对比度存在问题。
- 展开 图片对比度信息,该工具会建议补救措施。
- 点击右侧的箭头可查看下一项的信息。
|
|
- 在您的应用中,前往 Apply for GDG 界面,然后使用无障碍功能扫描仪应用扫描该界面。系统会提供许多建议,如下方左侧所示。确切地说,是 12 个。公平地说,其中一些是类似商品的重复评价。
- 点击底部工具栏中的“堆叠”图标
,即可获取所有建议的列表,如右侧屏幕截图所示。您将在本 Codelab 中解决所有这些问题。
|
|
Android 无障碍套件是 Google 推出的一系列应用,其中包含可帮助提高应用无障碍程度的工具。它包括 TalkBack 等工具。TalkBack 是一款屏幕阅读器,可提供听觉、触觉和语音反馈,让用户无需使用双眼即可在设备上浏览和使用内容。
事实证明,TalkBack 不仅供盲人使用,还供许多有某种视力障碍的人使用。甚至只是想让眼睛休息一下的人!
因此,无障碍功能适合所有人!在此任务中,您将试用 TalkBack 并更新应用,使其能够与 TalkBack 良好搭配使用。
第 1 步:安装并运行无障碍套件
TalkBack 预安装在许多实体设备上,但在模拟器上,您需要安装它。
- 打开 Play 商店。
- 找到无障碍套件。确保该应用是 Google 的正确应用。
- 如果未安装,请安装无障碍服务套装。
- 如需在设备上启用 TalkBack,请依次前往设置 > 无障碍功能,然后选择使用服务,开启 TalkBack。与无障碍扫描器一样,TalkBack 需要获得权限才能读取屏幕上的内容。接受权限请求后,TalkBack 会显示一个教程列表,教您如何有效使用 TalkBack。
- 请在此处暂停,并学习教程,即使只是为了了解如何在完成操作后重新关闭 TalkBack。
- 如需退出教程,请点击返回按钮以选择它,然后点按两次屏幕上的任意位置。
- 使用 TalkBack 探索 GDG Finder 应用。注意 TalkBack 未提供有关屏幕或控件的实用信息的地方。您将在下一个练习中修复此问题。
第 2 步:添加内容说明
内容描述标签是用于说明视图含义的描述性标签。大多数视图都应包含内容说明。
- 在 GDG Finder 应用运行且 TalkBack 已启用的情况下,前往申请运行 GDG 屏幕。
- 点按主图片,但没有任何反应。
- 打开 add_gdg_fragment.xml。
- 在
ImageView中,添加内容描述符属性,如下所示。strings.xml 中为您提供了stage_image_description字符串。
android:contentDescription="@string/stage_image_description"- 运行应用。
- 前往申请运行 GDG,然后点击相应图片。现在,您应该会听到该图片的简短说明。
- [可选] 为此应用中的其他图片添加内容说明。在正式版应用中,所有图片都需要有内容说明。
第 3 步:向可修改的文本字段添加提示
对于可编辑的元素(例如 EditText),您可以在 XML 中使用 android:hint 来帮助用户了解要输入的内容。提示始终在界面中显示,因为它是输入字段中的默认文本。
- 仍在 add_gdg_fragment.xml 中。
- 添加内容说明和提示,并使用以下代码作为指导。
添加到 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"- 为
labelTextWhy添加内容说明。
android:contentDescription="@string/motivation" - 为
EditTextWhy添加提示。为编辑框添加标签后,请为标签添加内容说明,并为该框添加提示。
android:hint="@string/enter_motivation"- 为提交按钮添加内容说明。所有按钮都需要提供说明,说明按下按钮后会发生什么。
android:contentDescription="@string/submit_button_description"- 在启用 TalkBack 的情况下运行应用,然后填写表单以申请运行 GDG。
第 4 步:创建内容组
对于 TalkBack 应视为一组的界面控件,您可以使用内容分组。分组在一起的相关内容会一起公布。这样一来,辅助技术的用户就不需要经常滑动、扫描或等待,即可发现屏幕上的所有信息。这不会影响控件在屏幕上的显示方式。
如需对界面组件进行分组,请将其封装到 ViewGroup(例如 LinearLayout)中。在 GDG Finder 应用中,labelTextWhy 和 editTextWhy 元素在语义上属于同一组,因此非常适合分组。
- 打开 add_gdg_fragment.xml。
- 将
LinearLayout环绕在LabelTextWhy和EditTextWhy周围,以创建内容组。复制并粘贴以下代码。此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/>- 依次选择 Code > Reformat code,以正确缩进所有代码。
- 从
labelTextWhy和editTextWhy中移除所有布局边距。 - 在
labelTextWhy中,将layout_constraintTop_toTopOf约束条件更改为contentGroup。
app:layout_constraintTop_toTopOf="@+id/contentGroup" />- 在
editTextWhy中,将layout_constraintBottom_toBottomOf约束更改为contentGroup。
app:layout_constraintBottom_toBottomOf="@+id/contentGroup"- 将
EditTextRegion和Button约束到contentGroup,以消除错误。
app:layout_constraintBottom_toTopOf="@+id/contentGroup"- 为
LinearLayout添加边距。(可选)将此边际提取为维度。
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"如果您需要帮助,请对照解决方案代码中的 add_gdg_fragment.xml 检查您的代码。
- 运行应用,并使用 TalkBack 浏览申请运行 GDG 界面。
第 5 步:添加实时区域
目前,提交按钮上的标签为确定。最好是,在提交表单之前,按钮有一个标签和说明;在用户点击并提交表单后,动态更改为其他标签和内容说明。您可以使用实时区域来实现此目的。
动态区域用于向无障碍服务表明,当视图发生变化时是否应通知用户。例如,告知用户密码不正确或出现网络错误是提高应用无障碍性的好方法。在此示例中,为简单起见,您会在提交按钮更改其状态时通知用户。
- 打开 add_gdg_fragment.xml。
- 使用提供的
submit字符串资源将按钮的文本分配更改为 Submit。
android:text="@string/submit"- 通过设置
android:accessibilityLiveRegion属性,为按钮添加实时区域。在您输入时,系统会提供多个值选项。您可以根据更改的重要性选择是否中断用户。如果值为“assertive”,无障碍服务会中断正在进行的语音,立即宣布此视图的更改。如果您将该值设置为“none”,则不会公布任何更改。设置为“礼貌”时,无障碍服务会宣布更改,但会等待轮到自己。将值设置为“polite”。

android:accessibilityLiveRegion="polite"- 在 add 软件包中,打开 AddGdgFragment.kt。
- 在
showSnackBarEventObserver中,在您完成SnackBar的显示后,为按钮设置新的内容说明和文本。
binding.button.contentDescription=getString(R.string.submitted)
binding.button.text=getString(R.string.done)- 运行应用,然后点击按钮。很遗憾,按钮和字体太小了!
第 6 步:修复按钮样式
- 在 add_gdg_fragment.xml 中,将按钮的
width和height更改为wrap_content,以便显示完整标签,并使按钮大小合适。
android:layout_width="wrap_content"
android:layout_height="wrap_content"- 从按钮中删除
backgroundTint、textColor和textSize属性,以便应用使用更好的主题样式。 - 从
textViewIntro中删除textColor属性。主题颜色应提供良好的对比度。 - 运行应用。请注意,提交按钮的可用性大大提高。点击提交,并注意它如何变为完成。
|
|
信息块是表示属性、文本、实体或操作的紧凑元素。用户可以通过这些组件输入信息、选择选项、过滤内容或触发操作。
Chip widget 是 ChipDrawable 周围的精简视图封装容器,其中包含所有布局和绘制逻辑。添加了额外的逻辑来支持触控、鼠标、键盘和无障碍导航。主功能块和关闭图标被视为单独的逻辑子视图,包含各自的导航行为和状态。

条状标签使用可绘制对象。借助 Android 可绘制对象,您可以在屏幕上绘制图片、形状和动画,这些对象可以具有固定大小,也可以动态调整大小。您可以使用图片作为可绘制对象,例如 GDG 应用中的图片。您还可以使用矢量绘图来绘制任何您能想到的内容。还有一种可调整大小的可绘制对象,称为 9-patch 可绘制对象,此 Codelab 中未介绍。drawable/ic_gdg.xml 中的 GDG 徽标是另一个可绘制对象。
可绘制对象不是视图,因此您无法将可绘制对象直接放在 ConstraintLayout 内,而需要将其放在 ImageView 内。您还可以使用可绘制对象为文本视图或按钮提供背景,并且背景会绘制在文本后面。
第 1 步:向 GDG 列表添加 chip
以下已勾选的 chip 使用了三个可绘制对象。背景和对勾标记都是可绘制对象。触摸微芯片会产生波纹效果,这是通过特殊的 RippleDrawable 实现的,该对象会根据状态变化显示波纹效果。

在此任务中,您将向 GDG 列表添加 chip,并让它们在被选中时更改状态。在此练习中,您将在搜索屏幕顶部添加一行称为微件的按钮。每个按钮都会过滤 GDG 列表,以便用户仅收到所选区域的结果。选择按钮后,按钮会更改其背景并显示对勾标记。
- 打开 fragment_gdg_list.xml。
- 在
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"/>- 在 layout 文件夹中,创建一个名为 region.xml 的新布局资源文件,用于定义一个
Chip的布局。 - 在 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"/>- 如需创建缺少的
selected_highlight颜色,请将光标放在selected_highlight上,调出意图菜单,然后为所选突出显示创建颜色资源。默认选项没有问题,因此只需点击确定即可。系统会在 res/color 文件夹中创建该文件。 - 打开 res/color/selected_highlight.xml。在此颜色状态列表(编码为
<selector>)中,您可以为不同的状态提供不同的颜色。每种状态和关联的颜色都编码为<item>。如需详细了解这些颜色,请参阅颜色主题。
- 在
<selector>内,向状态列表添加一个具有默认颜色colorOnSurface的项。在状态列表中,务必要始终涵盖所有状态。一种方法是设置默认颜色。
<item android:alpha="0.18" android:color="?attr/colorOnSurface"/>- 在默认颜色上方,添加一个颜色为
colorPrimaryVariant的item,并将其使用限制为仅在所选状态为true时使用。系统会从上到下处理状态列表,就像处理 case 语句一样。如果没有任何状态匹配,则应用默认状态。
<item android:color="?attr/colorPrimaryVariant"
android:state_selected="true" />第 2 步:显示 chip 行
GDG 应用会创建一个显示有 GDG 的地区的 chip 列表。选择某个功能块后,应用会过滤结果,仅显示该区域的 GDG 结果。
- 在 search 软件包中,打开 GdgListFragment.kt。
- 在
onCreateView()中,在return语句的正上方,添加viewModel.regionList的观察器并替换onChanged()。当视图模型提供的地区列表发生变化时,需要重新创建 chip。添加一条语句,以便在提供的data为null时立即返回。
viewModel.regionList.observe(viewLifecycleOwner, object: Observer<List<String>> {
override fun onChanged(data: List<String>?) {
data ?: return
}
})- 在
onChanged()内,在 null 测试下方,将binding.regionList分配给一个名为chipGroup的新变量,以缓存regionList。
val chipGroup = binding.regionList- 在下方,创建一个新的
layoutInflator,用于从chipGroup.context中扩充 chip。
val inflator = LayoutInflater.from(chipGroup.context)- 清理并重建项目,以消除数据绑定错误。
在膨胀器下方,您现在可以创建实际的 chip,regionList 中的每个区域对应一个 chip。
- 创建一个变量
children,用于存放所有 chip。为其分配传入的data上的映射函数,以创建并返回每个 chip。
val children = data.map {} - 在 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
}
}- 在 lambda 中,在返回
chip之前,添加一个点击监听器。当点击chip时,将其状态设置为checked。在viewModel中调用onFilterChanged(),这会触发一系列事件,以获取相应过滤条件的结果。
chip.setOnCheckedChangeListener { button, isChecked ->
viewModel.onFilterChanged(button.tag as String, isChecked)
}- 在 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)
}
}
})
- 运行应用并搜索 GDGS,打开搜索界面,以使用新芯片。点击每个功能块后,应用会在下方显示过滤条件组。

夜间模式可让应用将其颜色更改为深色主题,例如,当设备设置已设置为启用夜间模式时。在夜间模式下,应用会将其默认的浅色背景更改为深色,并相应地更改所有其他屏幕元素。
第 1 步:启用夜间模式
如需为应用提供深色主题,请将其主题从 Light 主题更改为名为 DayNight 的主题。DayNight 主题会根据模式显示为浅色或深色。
- 在
styles.xml,中,将AppTheme父主题从Light更改为DayNight。
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">- 在
MainActivity的onCreate()方法中,调用AppCompatDelegate.setDefaultNightMode()以通过编程方式开启深色主题。
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)- 运行应用,并验证应用是否已切换到深色主题。

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

Android Studio 项目:GDGFinderFinal。
支持从右向左书写的语言
- 在 Android 清单中,设置
android:supportsRtl="true"。 - 您可以在模拟器中预览 RTL,并使用自己的语言检查屏幕布局。在设备或模拟器上,打开设置,然后在开发者选项中选择强制使用从右到左的布局。
- 将对
Left和Right的引用替换为对Start和End的引用。 - 通过删除
android:autoMirrored="true"停用可绘制对象的镜像。 - 选择 Refactor > Add RTL support where possible ,让 Android Studio 为您完成这项工作。
- 使用 values-"语言代码" 文件夹存储特定于语言的资源。
扫描无障碍功能问题
- 在 Play 商店中,获取 Google LLC 的无障碍功能扫描仪,然后运行该扫描仪以扫描需要改进的屏幕元素。
以内容说明为设计宗旨,打造 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 组。
支持深色模式
- 使用
DayNightAppTheme支持深色模式。 - 您可以通过编程方式设置深色模式:
AppCompatDelegate.setDefaultNightMode() - 创建 res/values-night 资源文件夹,以提供深色模式的自定义颜色和值。
Android 开发者文档:
LayoutDirection(RTL)- 双向性
- 开始使用无障碍功能扫描仪
- TalkBack
- TalkBack 手势
- 可绘制对象概览文档
- 内容描述标签
- 内容分组
- 实时区域
- NinePatch 可绘制对象
- Draw 9-patch 工具
- 芯片
ChipGroup- 深色主题
- 颜色主题
- Color Tool
- 为可绘制图形添加动画
其他资源:
- 使用 Kotlin 开发 Android 应用(Udacity 课程)
- 面向编程人员的 Kotlin 训练营(Udacity 课程)
- 面向编程人员的 Kotlin 训练营 Codelab
此部分列出了在由讲师主导的课程中,学生学习此 Codelab 后可能需要完成的家庭作业。讲师自行决定是否执行以下操作:
- 根据需要布置作业。
- 告知学生如何提交家庭作业。
- 给家庭作业评分。
讲师可以酌情采纳这些建议,并且可以自由布置自己认为合适的任何其他家庭作业。
如果您是在自学此 Codelab,可随时通过这些家庭作业来检测您的知识掌握情况。
问题 1
要支持 RTL 语言,以下执行以下哪项操作?
▢ 将属性中的 Left 和 Right 替换为 Start 和 End
▢ 切换为 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 准则改变形状的屏幕区域
▢ 允许流式传输视频的视图
▢ 带动画的可绘制对象









