Compose 中的自动填充

某些应用(例如密码管理工具)可以使用用户提供的数据来填充其他应用中的组件。填充其他应用组件的应用称为自动填充服务。自动填充框架负责管理应用与自动填充服务之间的通信。

填写凭证和表单是一项非常耗时且容易出错的任务。自动填充功能可帮助用户节省填写字段的时间,并最大限度地减少用户输入错误。

只需几行代码,您就可以在 Compose 中实现自动填充功能。此功能可为用户带来以下好处:

填写凭据

自动填充功能可让用户通过以下方式填充凭据:

  • 当用户点按设置了自动填充语义的字段时,系统会向用户显示自动填充建议。
  • 系统会向用户显示自动填充建议,并根据用户输入的内容过滤这些建议。

保存凭据

用户可以通过自动填充功能以以下方式保存凭据:

  • 当用户在启用自动填充功能的字段中输入新信息或更新信息时,系统会触发保存对话框,提示用户保存信息。您可以通过以下两种方式节省费用:
    • 明确地提交信息(例如,通过点击按钮)
    • 隐式,当用户离开网页时
  • 根据凭据提供方的不同,当字段设置了 ContentType.NewPassword 时,系统可能会向用户建议使用安全系数高的密码。

您可以在应用中使用自动填充功能,以便用户更轻松地检索已保存的数据。自动填充通过 BasicTextField 支持文本组件以及基于该组件构建的所有 Material 文本字段。

设置自动填充

在设备或模拟器上使用自动填充 API 之前,您必须在“设置”中启用自动填充功能。您可以在此处指定一个凭据提供程序,供自动填充功能用来存储您的凭据。

一个设置页面,显示了如何指定凭据提供程序。
图 1. 一个设置页面,显示了如何指定凭据提供程序。

使用内容类型向文本字段添加自动填充功能

如需指明 TextField 已启用自动填充功能,请设置 ContentType 语义,并指定相应字段可接受的类型。这会向自动填充服务指明哪些类型的用户数据可能与此特定字段相关。使用 ContentType.Username 设置 TextField,以便用户可以使用自己的用户名进行填写。

通过设置 ContentType 语义,您的用户可以访问已保存在设备凭据提供程序中的自动填充信息。例如,如果用户已通过笔记本电脑上的 Chrome 浏览器登录您的应用,并通过凭据提供方保存了密码,那么系统会通过自动填充功能向用户提供其凭据。

基于价值的文本字段

TextField(
    value = textFieldValue.value,
    onValueChange = {textFieldValue.value = it},
    modifier = Modifier.semantics { contentType = ContentType.Username }
)

基于状态的文本字段

TextField(
    state = rememberTextFieldState(),
    modifier = Modifier.semantics { contentType = ContentType.Username }
)

添加具有多种类型的自动填充字段

在某些情况下,您可能希望 TextField 承担多个 ContentType。例如,登录字段可以接受电子邮件地址或用户名。您可以使用 + 运算符向 TextField 添加多种内容类型。

如需查看可使用自动填充功能保存的所有类型的数据,请参阅 ContentType 参考文档

基于价值的文本字段

TextField(
    value = textFieldValue.value,
    onValueChange = { textFieldValue.value = it },
    modifier = Modifier.semantics {
        contentType = ContentType.Username + ContentType.EmailAddress
    }
)

基于状态的文本字段

TextField(
    state = rememberTextFieldState(),
    modifier = Modifier.semantics {
        contentType = ContentType.Username + ContentType.EmailAddress
    }
)

使用自动填充功能填充数据

TextField 中添加 ContentType 后,您无需执行任何其他操作,用户即可填写凭据。

当用户点击启用了自动填充功能的字段时,如果系统存储了相关数据,则会在键盘上方的工具栏中显示一个提示用户填写凭据的 chip。

文本工具栏中显示已保存凭据的 chip。
图 2. 文本工具栏中显示已保存凭据的 chip。

通过导航使用自动填充功能节省数据流量

Compose 会自动尝试确定用户何时从网页导航离开并提交输入的凭据。一旦某个字段启用了自动填充功能,当用户离开相应网页时,系统就会自动保存凭据信息,而无需任何额外的代码。

使用自动填充功能显式保存数据

如需通过支持自动填充功能的文本字段显式保存新凭据,自动填充管理器应提交(或取消)自动填充上下文。然后,本地自动填充管理器会在必要时与自动填充框架通信。如果您想移除用户已输入的凭据,请调用 AutofillManager.cancel 以删除所有待处理数据,而不保存这些数据。

以下代码段展示了如何使用按钮通过自动填充功能显式保存数据:

  1. 创建一个本地变量来保存自动填充管理器,该变量可通过以下方式检索:

    val autofillManager = LocalAutofillManager.current

  2. TextField(s) 中,通过 Modifier.semantics 添加所选内容类型:

    • 对于基于值的文本字段:

      val autofillManager = LocalAutofillManager.current
      
      Column {
          TextField(
              value = textFieldValue.value,
              onValueChange = { textFieldValue.value = it },
              modifier = Modifier.semantics { contentType = ContentType.NewUsername }
          )
      
          Spacer(modifier = Modifier.height(16.dp))
      
          TextField(
              value = textFieldValue.value,
              onValueChange = { textFieldValue.value = it },
              modifier = Modifier.semantics { contentType = ContentType.NewPassword }
          )
      }

    • 使用基于状态的文本字段:

      val autofillManager = LocalAutofillManager.current
      
      Column {
          TextField(
              state = rememberTextFieldState(),
              modifier = Modifier.semantics { contentType = ContentType.NewUsername }
          )
      
          Spacer(modifier = Modifier.height(16.dp))
      
          TextField(
              state = rememberTextFieldState(),
              modifier = Modifier.semantics { contentType = ContentType.NewPassword }
          )
      }

  3. 根据需要通过点击按钮提交自动填充上下文:

    • 对于基于值的文本字段:

      val autofillManager = LocalAutofillManager.current
      
      Column {
          TextField(
              value = textFieldValue.value,
              onValueChange = { textFieldValue.value = it },
              modifier = Modifier.semantics { contentType = ContentType.NewUsername },
          )
      
          Spacer(modifier = Modifier.height(16.dp))
      
          TextField(
              value = textFieldValue.value,
              onValueChange = { textFieldValue.value = it },
              modifier = Modifier.semantics { contentType = ContentType.NewPassword },
          )
      
          // Submit button
          Button(onClick = { autofillManager?.commit() }) { Text("Reset credentials") }
      }

    • 使用基于状态的文本字段:

      val autofillManager = LocalAutofillManager.current
      
      Column {
          TextField(
              state = rememberTextFieldState(),
              modifier = Modifier.semantics { contentType = ContentType.NewUsername },
          )
      
          Spacer(modifier = Modifier.height(16.dp))
      
          TextField(
              state = rememberTextFieldState(),
              modifier = Modifier.semantics { contentType = ContentType.NewPassword },
          )
      
          // Submit button
          Button(onClick = { autofillManager?.commit() }) { Text("Reset credentials") }
      }

每当用户离开屏幕时,系统都会调用 Commit。如果提交按钮已关联到导航,则无需调用提交。如果您仍希望点击提交来触发保存对话框,请在此处添加 Commit

当用户点击该按钮时,系统会显示以下底部动作条,提示用户将凭据保存到所选的凭据提供方:

提示用户保存密码的底部动作条。
图 3. 提示用户保存密码的底部动作条。

通过建议使用安全系数高的密码,使用自动填充功能保存数据

根据您的凭据提供方,当您使用 NewUsernameNewPassword 内容类型时,用户可能会在键盘中看到用于建议安全系数高的密码的按钮。当用户点击此按钮时,系统会显示一个底部动作条,供用户保存凭据。您无需再实现任何其他功能,用户即可获得此体验。

键盘工具栏中的“建议一个安全系数高的密码”功能块。
图 4. 键盘工具栏中的“建议一个安全系数高的密码”功能块。
提示用户使用安全系数高的密码的底部工作表。
图 5. 提示用户使用安全系数高的密码的底部动作条。

问题排查

在调用“保存”用户历程时,如果您多次点击“暂不保存”,您的凭据提供方可能不再显示底部工作表。如需重新启用此功能并让系统再次显示此提示,您需要移除已屏蔽“记住此密码吗?”的特定应用。

提示用户保存密码的底部动作条。
图 6. 提示用户保存密码的底部工作表。

进一步自定义自动填充

在典型的自动填充用户历程中,当启用了自动填充功能的组件填充了凭据后,它会改变颜色并突出显示,以向用户表明自动填充已成功完成。

如需自定义此突出显示颜色,请使用 CompositionLocal 并提供您想要的任何颜色。默认的自动填充突出显示颜色定义为 Color(0x4dffeb3b)

基于价值的文本字段

val customHighlightColor = Color.Red

CompositionLocalProvider(LocalAutofillHighlightColor provides customHighlightColor) {
    TextField(
        value = textFieldValue.value,
        onValueChange = { textFieldValue.value = it },
        modifier = Modifier.semantics { contentType = ContentType.Username }
    )
}

基于状态的文本字段

val customHighlightColor = Color.Red

CompositionLocalProvider(LocalAutofillHighlightColor provides customHighlightColor) {
    TextField(
        state = rememberTextFieldState(),
        modifier = Modifier.semantics { contentType = ContentType.Username }
    )
}