Jetpack Compose中旋转后恢复键盘

3

我的应用程序屏幕上有一个TextField。当我旋转设备时,文本字段会保留值,但软键盘焦点不会。

我该如何保持焦点并防止键盘消失?

这是屏幕的简化版本的可组合项:

@Composable
fun LoginScreen(
    uiState: LoginUiState,
) {
    MyTheme {
        Surface(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(scrollableState)
                .imePadding(),
        ) {
            Column(
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                TextField(
                    value = uiState.email,
                    enabled = !uiState.isLoggingIn
                )
            }
        }
    }
}

UI状态来自于模型。
5个回答

1

您需要使用rememberSaveable来存储TextField之前是否聚焦。

  val focusRequester = remember { FocusRequester() }
    var hasFocus by rememberSaveable { mutableStateOf(false) }
    
    TextField(
        value = ...,
        onValueChange = { ... },
        modifier = Modifier.focusRequester(focusRequester).onFocusChanged {
            hasFocus = it.hasFocus
        }
    )
    LaunchedEffect(hasFocus){
        if(hasFocus) {
            focusRequester.requestFocus()
        }
    }
    

不需要使用focusRequestor来保存。如果它被重新创建也没关系。你只需要与TextField的值一起使用该值。此外,不需要将“hasFocus”作为副作用的键传递。这会在焦点改变时导致不必要的代码调用,而我们只想在配置更改后重新获得焦点。 - Richard Onslow Roper
我们需要仅在副作用启动时调用,并且是在焦点更改时调用。如果重新组合是由其他原因触发的,LaunchedEffect将防止多次请求焦点。 - Nikola Despotoski
不,即使您不使用副作用,也不会在重组上反复请求焦点。这就是remember的作用,对吗? - Richard Onslow Roper
1
谢谢。你的回答和@MARSK的补充为我提供了足够的信息来实现所需的行为。同时,你提供的代码还不够。在我的测试中,hasFocus在旋转之前被重置。旋转后,LaunchedEffect块什么也没做。我不得不实现一种更复杂的方法来检测实际上有焦点的内容,并将这些信息保存在我的模型中。 - Bobrovsky
1
@Bobrovsky,你能分享一下最终的成果吗?谢谢。 - Eyjafl

1

我猜你在这里使用了类似ViewModel的状态保留器。你可以像Nikola建议的那样将值存储在rememberSaveable块中,或者你可以简单地在放置uiState参数的位置放置一个简单的布尔值。这种情况下不需要使用MutableState<T>。同时也不需要任何副作用,只需创建一个参数即可。

@Composable
fun MyFiled(
 loginState: ... , 
 isFocused: Boolean
){
 if (isFocused)
   focusRequestor.requestFocus()
 ... 
}

只需设置一个简单的条件,就可以完成。


1

我认为这与此有关于TextField在配置更改时失去焦点的问题有关。

一个可能的临时解决方案是使用rememberSaveable将修改器onFocusChanged的焦点布尔值变化保存到变量中,然后使用LaunchedEffect在进入组合时将焦点恢复到TextField

val focusRequester = remember { FocusRequester() }
var isFocused by rememberSaveable { mutableStateOf(false) }

LaunchedEffect(true) {
    if (isFocused) {
        focusRequester.requestFocus()
    }
}

var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier
        .focusRequester(focusRequester)
        .onFocusChanged {
            isFocused = it.isFocused
        }
)

然而,这个解决方案似乎不起作用,因为当 TextField 进入组合时,onFocusChanged 立即被调用并将 isFocused 更新为 false,在 LaunchedEffect 有机会处理前一个值之前。

因此,可以使用另一个变量来存储从 isFocused 中获取的值,然后再更新。下面我将其命名为 wasFocused

val focusRequester = remember { FocusRequester() }
var isFocused by rememberSaveable { mutableStateOf(false) }
val wasFocused = remember { isFocused }

LaunchedEffect(true) {
    if (wasFocused) {
        focusRequester.requestFocus()
    }
}

var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier
        .focusRequester(focusRequester)
        .onFocusChanged {
            isFocused = it.isFocused
        }
)

在我的 Android 模拟器上旋转设备后,这个方法可以正常工作。如果在您的设备上无法恢复焦点,请告诉我。

(需要说明的是,这个解决方案适用于简单情况。如果在更复杂的情况下使用,例如在像 LazyColumn 中有许多 TextField 的情况下,可能会出现问题。我有这样的情况,并将其作为我的解决方案的一部分使用)。


0

你也可以在你的活动清单中设置这个:

<activity
    android:name=".MyActivity"
    android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"

在Compose的世界中,您不需要销毁活动。在配置更改时,您会发现可组合重新组成。以上修复了保留键盘状态的问题。

0
你可以使用这段代码,它使用focusRequester在屏幕方向变化后改变焦点。
val focusRequester = remember { FocusRequester() }
var isFirstComposition by rememberSaveable { mutableStateOf(true) }
TextField(
    value = ...,
    onValueChange = { ... },
    modifier = Modifier.focusRequester(focusRequester)
)
LaunchedEffect(focusRequester) {
    if (!isFirstComposition) {
        focusRequester.requestFocus()
    }
    isFirstComposition = false
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接