如何在关闭键盘时清除 TextField 焦点并防止在 Jetpack Compose 中退出应用程序时需要按两次后退?

Phi*_*hov 21 android android-jetpack-compose android-jetpack-compose-text

我正在使用BasicTextField。

当我开始编辑时,后退按钮变成隐藏键盘按钮(向下箭头)。

第一次按后退按钮会隐藏键盘,但焦点仍然在文本字段上。两者onFocusChanged和BackPressHandler处理程序都没有被调用。

第二次按后退按钮可清除焦点:onFocusChanged被调用且未BackPressHandler被调用。

BackHandler {
    println("BackPressHandler")
}
val valueState = remember { mutableStateOf(TextFieldValue(text = "")) }
BasicTextField(
    value = valueState.value,
    onValueChange = {
        valueState.value = it
    },
    modifier = Modifier
        .fillMaxWidth()
        .onFocusChanged {
            println("isFocused ${it.isFocused}")
        }
)
Run Code Online (Sandbox Code Playgroud)

第三次 BackHandler 工作正常。只是用它来测试,我在这里不应该需要它,它预计在第一次点击后退按钮后焦点会丢失

Phi*_*hov 28

焦点文本字段存在撰写问题,当键盘隐藏时,后退按钮无法关闭应用程序。它被标记为已修复,但将包含在未来的某个版本中,而不是包含在1.0

但是,据我了解,键盘被关闭后文本字段不会失去焦点,这是 Android 上的预期行为(因为可能连接了键盘?我不明白原因)。这也是它在旧的 Android 布局中的工作原理

这对我来说似乎很奇怪,所以我使用了以下修饰符,当键盘消失时它会放弃焦点:

fun Modifier.clearFocusOnKeyboardDismiss(): Modifier = composed {
    var isFocused by remember { mutableStateOf(false) }
    var keyboardAppearedSinceLastFocused by remember { mutableStateOf(false) }
    if (isFocused) {
        val imeIsVisible = WindowInsets.isImeVisible
        val focusManager = LocalFocusManager.current
        LaunchedEffect(imeIsVisible) {
            if (imeIsVisible) {
                keyboardAppearedSinceLastFocused = true
            } else if (keyboardAppearedSinceLastFocused) {
                focusManager.clearFocus()
            }
        }
    }
    onFocusEvent {
        if (isFocused != it.isFocused) {
            isFocused = it.isFocused
            if (isFocused) {
                keyboardAppearedSinceLastFocused = false
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

BasicTextField(
    value = valueState.value,
    onValueChange = {
        valueState.value = it
    },
    modifier = Modifier
        .clearFocusOnKeyboardDismiss()
)
Run Code Online (Sandbox Code Playgroud)

  • @mmm111mmm 你遵循了“伴奏插图”的“WindowCompat.setDecorFitsSystemWindows(window, false)”注释吗? (2认同)

Shr*_*til 19

最新更新

此问题已在最新版本的 Jetpack Compose 中得到修复。所以不需要手动处理。


旧答案

感谢这里所有的答案。参考此处的答案后,这是一个不使用任何库的解决方案

1.在View上创建一个扩展来判断键盘是否打开

fun View.isKeyboardOpen(): Boolean {
    val rect = Rect()
    getWindowVisibleDisplayFrame(rect);
    val screenHeight = rootView.height
    val keypadHeight = screenHeight - rect.bottom;
    return keypadHeight > screenHeight * 0.15
}
Run Code Online (Sandbox Code Playgroud)

2. 创建一个可观察状态来确定键盘是否打开

这将监听 LocalView 上的全局布局更新,其中在每个事件中,我们检查键盘打开/关闭状态。

@Composable
fun rememberIsKeyboardOpen(): State<Boolean> {
    val view = LocalView.current

    return produceState(initialValue = view.isKeyboardOpen()) {
        val viewTreeObserver = view.viewTreeObserver
        val listener = OnGlobalLayoutListener { value = view.isKeyboardOpen() }
        viewTreeObserver.addOnGlobalLayoutListener(listener)

        awaitDispose { viewTreeObserver.removeOnGlobalLayoutListener(listener)  }
    }
}
Run Code Online (Sandbox Code Playgroud)

3.创建修改器

此修改器将负责清除键盘可见/不可见事件的焦点。

fun Modifier.clearFocusOnKeyboardDismiss(): Modifier = composed {

    var isFocused by remember { mutableStateOf(false) }
    var keyboardAppearedSinceLastFocused by remember { mutableStateOf(false) }

    if (isFocused) {
        val isKeyboardOpen by rememberIsKeyboardOpen()

        val focusManager = LocalFocusManager.current
        LaunchedEffect(isKeyboardOpen) {
            if (isKeyboardOpen) {
                keyboardAppearedSinceLastFocused = true
            } else if (keyboardAppearedSinceLastFocused) {
                focusManager.clearFocus()
            }
        }
    }
    onFocusEvent {
        if (isFocused != it.isFocused) {
            isFocused = it.isFocused
            if (isFocused) {
                keyboardAppearedSinceLastFocused = false
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

4. 使用它

最后,将其与TextField可组合项一起使用

BasicTextField(Modifier.clearFocusOnKeyboardDismiss())
Run Code Online (Sandbox Code Playgroud)