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 后,您无需执行任何其他操作,用户即可填写凭据。

用户点击启用了自动填充功能的字段后,如果系统存储了相关数据,则用户会在键盘上方的工具栏中看到一个提示他们填写凭据的条状标签。

文本工具栏中显示保存的凭据的条状标签。
图 2. 文本工具栏中显示已保存凭据的条状标签。

离开页面时使用自动填充功能保存数据

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。如果您仍想通过点击提交按钮来触发保存对话框,请在此处添加 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 }
    )
}