前言

输入数据是许多应用程序中的一项重要任务。在没有物理键盘的设备上(绝大多数在 Android 领域),所谓的软(软件)键盘处理用户输入。现在,您可能想知道为什么我们需要讨论这些虚拟外围设备。操作系统不应该照顾吗?我的意思是,就用户界面而言,该应用程序通过显示和配置可编辑的文本字段来表达其允许用户输入的愿望。还需要做什么?本文详细介绍了 Jetpack Compose 应用程序如何与键盘交互。

让我们从一个简单的 Compose 层次结构开始:

@Composable
fun KeyboardHandlingDemo1() {
  var text by remember { mutableStateOf(TextFieldValue()) }
  Column(
    modifier = Modifier.fillMaxSize(),
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.Bottom
  ) {
    Box(
      modifier = Modifier
        .padding(16.dp)
        .fillMaxSize()
        .background(color = MaterialTheme.colors.primary)
        .weight(1.0F),
      contentAlignment = Alignment.BottomCenter
    ) {
      Text(
        modifier = Modifier.padding(bottom = 16.dp),
        text = stringResource(id = R.string.app_name),
        color = MaterialTheme.colors.onPrimary,
        style = MaterialTheme.typography.h5
      )
    }
    TextField(modifier = Modifier.padding(bottom = 16.dp),
      value = text,
      onValueChange = {
        text = it
      })
  }
}


看起来不错,对吧?现在,让我们看看如果文本字段获得焦点会发生什么。

这当然看起来并不可怕,但也不是很好。由于文本字段可能会引起用户的注意,它应该是完全可见的,对吧?在这里,重要的是要了解软键盘如何与 Activity 以及显示 Activity 的窗口进行交互。几乎总是(从 API 级别 3 开始)有一个清单属性,windowSoftInputMode. 它属于.

属性

控制 Activity 的主窗口如何与包含屏幕软键盘的窗口交互。

主要有两个方面:

  • 当活动成为用户关注的焦点时,软键盘是否应该可见?
  • 当部分窗口被软键盘覆盖时,应该对 Activity 的主窗口进行哪些调整?

在本文中,我将重点介绍后一种。一般介绍请参考处理输入法可见性。

现在,让我们看看与调整相关的值。

adjustUnspecified是主窗口行为的默认设置。文件说:

未指定活动的主窗口是否调整大小以便为软键盘腾出空间,或者是否平移窗口的内容以使当前焦点在屏幕上可见。系统会根据窗口的内容是否有任何可以滚动其内容的布局视图来自动选择其中一种模式。如果存在这样的视图,则窗口将被调整大小,假设滚动可以使窗口的所有内容在较小的区域内可见。

adjustResize:

Activity 的主窗口总是调整大小,以便为屏幕上的软键盘腾出空间。

adjustPan:

Activity
的主窗口没有调整大小来为软键盘腾出空间。相反,窗口的内容会自动平移,因此当前焦点永远不会被键盘遮挡,用户始终可以看到他们正在输入的内容。这通常不如调整大小可取,因为用户可能需要关闭软键盘才能到达窗口的模糊部分并与之交互。

如果你回想上一个截图,窗口显然没有调整大小。所以, …

系统会根据窗口的内容是否有任何可以滚动其内容的布局视图来自动选择其中一种模式。

… 似乎不适用于 Compose 应用程序。这当然不足为奇,因为使用setContent { … }is显示的 Compose 层次结构的根视图ComposeView,它 extends AbstractComposeView,它又扩展ViewGroup(它不能滚动)。

所以,修复很简单:只需添加

android:windowSoftInputMode="adjustResize"

到你的标签。

多个文本字段

让我们看看另一个 Compose 层次结构:

@Composable
fun KeyboardHandlingDemo2() {
  val states = remember {
    mutableStateListOf("1", "2", "3", 
        "4", "5", "6", "7", "8", "9", "10")
  }
  val listState = rememberLazyListState()
  LazyColumn(
    state = listState,
    modifier = Modifier.fillMaxSize(),
    horizontalAlignment = Alignment.CenterHorizontally
  ) {
    itemsIndexed(states) { i, _ ->
      OutlinedTextField(value = states[i],
        modifier = Modifier.padding(top = 16.dp),
        onValueChange = {
          states[i] = it
        },
        label = {
          Text("Text field ${i + 1}")
        })
    }
  }
}


用户界面包含相当多的可编辑文本字段。但是,通过上述实现,用户不能使用软键盘移动到下一个字段,而必须单击并滚动。幸运的是,我们可以使用 Compose 键盘操作和选项轻松实现这一点。将以下行添加到对 的调用中OutlinedTextField():

keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
  onNext = {
    focusManager.moveFocus(FocusDirection.Down)
  }
),

我们将软键盘配置为显示一个特殊的Next键 ( ),并在 的回调中ImeAction.Next使用一个FocusManager(属于 package androidx.compose.ui.focus)导航(移动焦点)到(垂直)下一个文本字段。

onNextKeyboardActions
val focusManager = LocalFocusManager.current

很酷,对吧?不过,我们还需要做一件事。我们的文本字段属于可滚动列表。移动焦点不会更改当前可见的列表部分。以下是如何做到这一点:

listState.animateScrollToItem(i)

animateScrollToItem()是一个挂起函数,所以它应该从协程或另一个挂起函数中调用。

coroutineScope.launch {
  listState.animateScrollToItem(i)
}

最后,要在可组合函数中获得协程范围,您可以使用rememberCoroutineScope():

val coroutineScope = rememberCoroutineScope()

我还想向您展示一件事:如何关闭软键盘。

显示和隐藏软键盘

以下屏幕截图显示了我的KeyboardHandlingDemo3()可组合函数。它允许用户在按下计算按钮或软键盘上的特殊完成键后输入一个数字并计算其平方。您在屏幕截图中看不到的内容:软键盘已关闭。这可能是希望在输入数据后再次显示完整的用户界面。

让我们看一下代码:

@ExperimentalComposeUiApi
@Composable
fun KeyboardHandlingDemo3() {
  val kc = LocalSoftwareKeyboardController.current
  var text by remember { mutableStateOf("") }
  var result by remember { mutableStateOf("") }
  val callback = {
    result = try {
      val num = text.toFloat()
      num.pow(2.0F).toString()
    } catch (ex: NumberFormatException) {
      ""
    }
    kc?.hide()
  }
  Column(
    modifier = Modifier.fillMaxSize(),
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.Center
  ) {
    Row {
      TextField(modifier = Modifier
        .padding(bottom = 16.dp)
        .alignByBaseline(),
        keyboardOptions = KeyboardOptions(
          keyboardType = KeyboardType.Number,
          imeAction = ImeAction.Done
        ),
        keyboardActions = KeyboardActions(
          onDone = {
            callback()
          }
        ),
        value = text,
        onValueChange = {
          text = it
        })
      Button(modifier = Modifier
        .padding(start = 8.dp)
        .alignByBaseline(),
        onClick = {
          callback()
        }) {
        Text(stringResource(id = R.string.calc))
      }
    }
    Text(
      text = result,
      style = MaterialTheme.typography.h4
    )
  }
}

计算发生在callbacklambda 中。在这里,软键盘也通过调用hide()实例LocalSoftwareKeyboardController来关闭。请注意,此 API 是实验性的,可能会在未来的 Compose 版本中发生变化。

我们通过传递和to 来配置一个带有Done键的数字键盘。lambda 是从按钮的回调中调用的,里面属于.keyboardType = KeyboardType.NumberimeAction = ImeAction.DoneKeyboardOptions()callbackonClickonDoneKeyboardActions()

结论

在本文中,我向您展示了如何与 Compose 应用程序中的软键盘进行交互。我错过了什么?你想跟进吗?请在评论中分享您的想法。

GitHub 加速计划 / compose / compose
33.26 K
5.15 K
下载
compose - Docker Compose是一个用于定义和运行多容器Docker应用程序的工具,通过Compose文件格式简化应用部署过程。
最近提交(Master分支:1 个月前 )
73d3a25e Signed-off-by: Suleiman Dibirov <idsulik@gmail.com> 27 天前
3524bcfa Signed-off-by: Suleiman Dibirov <idsulik@gmail.com> 27 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐