Wear OS 上 Jetpack Compose 中的 BasicTextField 问题

Posted

技术标签:

【中文标题】Wear OS 上 Jetpack Compose 中的 BasicTextField 问题【英文标题】:Problem with BasicTextField in Jetpack Compose on Wear OS 【发布时间】:2022-01-03 00:45:21 【问题描述】:

我是 Compose 的新手,但在 Wear OS 上的输入文本字段有问题。 问题是我无法像通常在 android 上那样使用软键盘。此外,当我尝试在 XML 中实现相同的布局时 - 它起作用了。 因此,当我点击输入文本字段时,键盘会弹出然后隐藏。当我再次点击时 - 键盘会弹出并保持打开状态,但如果我尝试输入任何文本 - 输入字段(在键盘本身上)中不会出现任何内容,尽管输入的文本正在向下传递到 UI 上的输入文本字段。

当我点击输入文本字段以打开键盘时,这是我在模拟器上的日志中得到的:

2021-11-24 09:44:36.569 W/IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection
2021-11-24 09:44:36.571 W/IInputConnectionWrapper: getTextAfterCursor on inactive InputConnection
2021-11-24 09:44:36.649 W/RecordingIC: requestCursorUpdates is not supported

这是我在真实设备上得到的:

2021-11-24 09:35:39.783 W/IInputConnectionWrapper: getExtractedText on inactive InputConnection
2021-11-24 09:35:39.872 W/IInputConnectionWrapper: beginBatchEdit on inactive InputConnection
2021-11-24 09:35:39.873 W/IInputConnectionWrapper: endBatchEdit on inactive InputConnection
2021-11-24 09:35:39.873 W/IInputConnectionWrapper: setComposingRegion on inactive InputConnection
2021-11-24 09:35:39.873 W/IInputConnectionWrapper: beginBatchEdit on inactive InputConnection
2021-11-24 09:35:39.873 W/IInputConnectionWrapper: endBatchEdit on inactive InputConnection
2021-11-24 09:35:39.873 W/IInputConnectionWrapper: beginBatchEdit on inactive InputConnection
2021-11-24 09:35:39.873 W/IInputConnectionWrapper: endBatchEdit on inactive InputConnection
2021-11-24 09:35:39.873 W/IInputConnectionWrapper: getExtractedText on inactive InputConnection
2021-11-24 09:35:39.882 W/IInputConnectionWrapper: beginBatchEdit on inactive InputConnection
2021-11-24 09:35:39.883 W/IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection
2021-11-24 09:35:39.884 W/IInputConnectionWrapper: getTextAfterCursor on inactive InputConnection
2021-11-24 09:35:39.888 W/IInputConnectionWrapper: getSelectedText on inactive InputConnection
2021-11-24 09:35:39.890 W/IInputConnectionWrapper: endBatchEdit on inactive InputConnection
2021-11-24 09:35:39.891 W/IInputConnectionWrapper: beginBatchEdit on inactive InputConnection
2021-11-24 09:35:39.891 W/IInputConnectionWrapper: endBatchEdit on inactive InputConnection
2021-11-24 09:35:39.891 W/IInputConnectionWrapper: beginBatchEdit on inactive InputConnection
2021-11-24 09:35:39.891 W/IInputConnectionWrapper: endBatchEdit on inactive InputConnection

这是我的“可组合”:

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun ActivationScreen() 

    var key by remember  mutableStateOf("") 

    var isReady by remember 
        mutableStateOf(false)
    

    Column(modifier = Modifier
        .padding(40.dp)
        .fillMaxSize()
    ) 
        val keyboardController = LocalSoftwareKeyboardController.current
        val focusRequester = FocusRequester()
        BasicTextField(
            value = key,
            onValueChange = 
                //isReady = it.length>11
                key = it
            ,
            singleLine = true,
            keyboardOptions = KeyboardOptions.Default.copy(
                imeAction = ImeAction.Done
            ),
            keyboardActions = KeyboardActions(
                onDone = 
                    keyboardController?.hide()
                
            ),
            modifier = Modifier
                .size(140.dp, 20.dp)
                .background(Color.White)
                .align(Alignment.CenterHorizontally)
                //.focusRequester(focusRequester)
                //.focusOrder(focusRequester)
        )

        Text(
            text = "ACTIVATION",
        )

        val status = if (isReady) "READY" else "NOT READY"
        Text(
            text = status,
        )
    

【问题讨论】:

可穿戴设备上的文本输入并不是 Compose for WearOS 的优先事项,但预计最终会起作用。其他人报告了可重现的问题。在这里提出一个错误issuetracker.google.com/… 【参考方案1】:

您应该避免在 Wear 上输入文本,但如果您确实需要,GBoard 活动是最好的激活方式。

见https://developer.android.com/reference/androidx/wear/input/RemoteInputIntentHelper.Companion#createActionRemoteInputIntent()

@Composable
fun TextInput() 
  val label = remember  mutableStateOf("Start")
  val launcher =
    rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) 
      it.data?.let  data ->
        val results: Bundle = RemoteInput.getResultsFromIntent(data)
        val ipAddress: CharSequence? = results.getCharSequence("ip_address")
        label.value = ipAddress as String
      
    
  Column() 
    Spacer(modifier = Modifier.height(20.dp))
    Chip(
      label =  Text(label.value) ,
      onClick = 
    )
    Chip(
      label =  Text("Search with specific IP") ,
      onClick = 
        val intent: Intent = RemoteInputIntentHelper.createActionRemoteInputIntent();
        val remoteInputs: List<RemoteInput> = listOf(
          RemoteInput.Builder("ip_address")
            .setLabel("Manual IP Entry")
            .wearableExtender 
              setEmojisAllowed(false)
              setInputActionType(EditorInfo.IME_ACTION_DONE)
            .build()
        )

        RemoteInputIntentHelper.putRemoteInputsExtra(intent, remoteInputs)

        launcher.launch(intent)
      
    )
  

【讨论】:

Yuri,不幸的是,我别无选择 - 该应用程序在独立模式下工作,需要非常基本的文本输入。你的方法正是我想要的。谢谢! @Dmitry 您是否必须导入或安装其他一些库?我无法导入 RemoteInputIntentHelper @FranciscoHanna 我确实导入了 androidx.wear.input.RemoteInputIntentHelper 为了让 wearableExtender 工作,你必须在 wear-input 依赖的 1.2.0+ 版本上。从今天开始,应该使用 1.2.0-alpha02 版本。在您的应用程序 gradle 文件中,确保您具有依赖项 implementation 'androidx.wear:wear-input:1.2.0-alpha02'【参考方案2】:

根据@Yuri Schimke 的回答,这是一个接受占位符、值和 onChange 作为属性的通用组件实现。

import android.app.RemoteInput
import android.content.Intent
import android.os.Bundle
import android.view.inputmethod.EditorInfo
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.Text
import androidx.wear.input.RemoteInputIntentHelper
import androidx.wear.input.wearableExtender

@Composable
fun TextInput(
  placeholder: String,
  value: String?,
  onChange: (value: String) -> Unit,
) 
  val launcher =
    rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) 
      it.data?.let  data ->
        val results: Bundle = RemoteInput.getResultsFromIntent(data)
        val newValue: CharSequence? = results.getCharSequence(placeholder)
        onChange(newValue as String)
      
    
  Column() 
    Chip(
      label =  Text(if (value == null || value.isEmpty()) placeholder else value) ,
      onClick = 
        val intent: Intent = RemoteInputIntentHelper.createActionRemoteInputIntent();
        val remoteInputs: List<RemoteInput> = listOf(
          RemoteInput.Builder(placeholder)
            .setLabel(placeholder)
            .wearableExtender 
              setEmojisAllowed(false)
              setInputActionType(EditorInfo.IME_ACTION_DONE)
            .build()
        )

        RemoteInputIntentHelper.putRemoteInputsExtra(intent, remoteInputs)

        launcher.launch(intent)
      
    )
  

注意:这需要androidx.wear:wear-input:1.2.0+

【讨论】:

以上是关于Wear OS 上 Jetpack Compose 中的 BasicTextField 问题的主要内容,如果未能解决你的问题,请参考以下文章

Compose for Wear OS 1.1 推出稳定版: 了解新功能!

欢迎体验 | Wear OS 版 Compose 开发者预览版

使用 Compose 构建 Wear OS 应用

借助 Compose for Wear OS,Todoist 安装增长率提高了 50%

借助 Compose for Wear OS,Todoist 安装增长率提高了 50%

一起看 I/O | Compose for Wear OS Beta 版发布