在Jetpack Compose中,我发现唯一的方法是使用accompanist-insets
来去除窗口插图。但这样会导致我应用程序布局出现其他问题。
安卓系统的做法似乎是这样,我可以将其传递到我的Compose应用程序中并根据情况采取行动。
在Jetpack Compose中还有其他方法吗?
在Jetpack Compose中,我发现唯一的方法是使用accompanist-insets
来去除窗口插图。但这样会导致我应用程序布局出现其他问题。
安卓系统的做法似乎是这样,我可以将其传递到我的Compose应用程序中并根据情况采取行动。
在Jetpack Compose中还有其他方法吗?
使用新的WindowInsets
API,可以更加轻松地进行操作。
首先,要返回正确的值,需要设置:
WindowCompat.setDecorFitsSystemWindows(window, false)
然后将键盘用作状态:
@Composable
fun keyboardAsState(): State<Boolean> {
val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
return rememberUpdatedState(isImeVisible)
}
使用示例:
val isKeyboardOpen by keyboardAsState() // true or false
PS:我尝试过使用WindowInsets.isImeVisible,但第一次调用时它会返回true。
如果你想使用语句,我找到了这个解决方案:
enum class Keyboard {
Opened, Closed
}
@Composable
fun keyboardAsState(): State<Keyboard> {
val keyboardState = remember { mutableStateOf(Keyboard.Closed) }
val view = LocalView.current
DisposableEffect(view) {
val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
val rect = Rect()
view.getWindowVisibleDisplayFrame(rect)
val screenHeight = view.rootView.height
val keypadHeight = screenHeight - rect.bottom
keyboardState.value = if (keypadHeight > screenHeight * 0.15) {
Keyboard.Opened
} else {
Keyboard.Closed
}
}
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
onDispose {
view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener)
}
}
return keyboardState
}
而要检测/检查值,您只需要这样做:
val isKeyboardOpen by keyboardAsState() // Keyboard.Opened or Keyboard.Closed
isKeyboardOpen
对我来说显示了同一个值的快速变化。就像每秒钟有10个日志。 - Tran Hoai NamimePadding
来解决我的问题。我只需要找到一种根据键盘显示/隐藏来调整 UI 的方法,然后就找到了这个。 - Arst这里有一个解决方案,它使用OnGlobalLayoutListener
来监听布局变化,并使用新的窗口插入API进行计算,正如文档所推荐的。您可以将此代码放置在 @Composable
函数的任何位置,并根据需要处理isKeyboardOpen
。我测试过,在API 21及以上版本上可以正常工作。
val view = LocalView.current
val viewTreeObserver = view.viewTreeObserver
DisposableEffect(viewTreeObserver) {
val listener = ViewTreeObserver.OnGlobalLayoutListener {
val isKeyboardOpen = ViewCompat.getRootWindowInsets(view)
?.isVisible(WindowInsetsCompat.Type.ime()) ?: true
// ... do anything you want here with `isKeyboardOpen`
}
viewTreeObserver.addOnGlobalLayoutListener(listener)
onDispose {
viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
对我来说,其他解决方案都不太好用:键盘总是关闭的。
OnGlobalLayoutListener
的答案中,使用的公式似乎表现不如预期,并且使用了旧的 API。WindowInsetListener
的答案中,由于 view
不是根视图,因此不会应用任何窗口插入。我尝试将 view
替换为 view.rootView
,虽然键盘检测代码可以正常工作,但将根视图传递给 setOnApplyWindowInsetsListener
会替换组件设置的任何侦听器,这显然是不希望发生的。view.viewTreeObserver
而不是整个 view
,是吗?如果确认无误,我会更新我的答案。 - StypoxWindowInsest.ime
进行检查。WindowCompat.setDecorFitsSystemWindows(window, false)
。WindowInsets.isImeVisible
val offsetY = WindowInsets.ime.getBottom(density)
https://dev59.com/b8Tsa4cB1Zd3GeqPNv3P#73358604
当它打开时,它会返回诸如以下的值。17:40:21.429 I OffsetY: 1017
17:40:21.446 I OffsetY: 38
17:40:21.463 I OffsetY: 222
17:40:21.479 I OffsetY: 438
17:40:21.496 I OffsetY: 586
17:40:21.513 I OffsetY: 685
17:40:21.530 I OffsetY: 764
17:40:21.546 I OffsetY: 825
17:40:21.562 I OffsetY: 869
17:40:21.579 I OffsetY: 907
17:40:21.596 I OffsetY: 937
17:40:21.613 I OffsetY: 960
17:40:21.631 I OffsetY: 979
17:40:21.646 I OffsetY: 994
17:40:21.663 I OffsetY: 1004
17:40:21.679 I OffsetY: 1010
17:40:21.696 I OffsetY: 1014
17:40:21.713 I OffsetY: 1016
17:40:21.730 I OffsetY: 1017
17:40:21.746 I OffsetY: 1017
17:40:54.276 I OffsetY: 0
17:40:54.288 I OffsetY: 972
17:40:54.303 I OffsetY: 794
17:40:54.320 I OffsetY: 578
17:40:54.337 I OffsetY: 430
17:40:54.354 I OffsetY: 331
17:40:54.371 I OffsetY: 252
17:40:54.387 I OffsetY: 191
17:40:54.404 I OffsetY: 144
17:40:54.421 I OffsetY: 109
17:40:54.437 I OffsetY: 79
17:40:54.454 I OffsetY: 55
17:40:54.471 I OffsetY: 37
17:40:54.487 I OffsetY: 22
17:40:54.504 I OffsetY: 12
17:40:54.521 I OffsetY: 6
17:40:54.538 I OffsetY: 2
17:40:54.555 I OffsetY: 0
17:40:54.571 I OffsetY: 0
viewTreeObserver
找到了一种方法。 它本质上是Android版本,但它调用一个回调函数,在Compose中可以使用。class MainActivity : ComponentActivity() {
var kbGone = false
var kbOpened: () -> Unit = {}
var kbClosed: () -> Unit = {}
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setContent {
kbClosed = {
// dismiss the keyboard with LocalFocusManager for example
}
kbOpened = {
// something
}
MyComponent()
}
setupKeyboardDetection(findViewById<View>(android.R.id.content))
}
fun setupKeyboardDetection(contentView: View) {
contentView.viewTreeObserver.addOnGlobalLayoutListener {
val r = Rect()
contentView.getWindowVisibleDisplayFrame(r)
val screenHeight = contentView.rootView.height
val keypadHeight = screenHeight - r.bottom
if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
kbGone = false
kbOpened()
} else(!kbGone) {
kbGone = true
kbClosed()
}
}
}
}
@Composable
fun keyboardAsState(): State<Boolean> {
val view = LocalView.current
var isImeVisible by remember { mutableStateOf(false) }
DisposableEffect(LocalWindowInfo.current) {
val listener = ViewTreeObserver.OnPreDrawListener {
isImeVisible = ViewCompat.getRootWindowInsets(view)
?.isVisible(WindowInsetsCompat.Type.ime()) == true
true
}
view.viewTreeObserver.addOnPreDrawListener(listener)
onDispose {
view.viewTreeObserver.removeOnPreDrawListener(listener)
}
}
return rememberUpdatedState(isImeVisible)
}
keyboardAsState
,允许您观察和响应键盘可见性状态的变化。它通过利用DisposableEffect
和ViewTreeObserver.OnPreDrawListener
来检测键盘打开或关闭的时机。val isKeyboardOpen by keyboardAsState()
访问即可。它将返回一个反映键盘当前是否可见的State<Boolean>
。@Composable
fun isKeyboardVisible(): Boolean = WindowInsets.ime.getBottom(LocalDensity.current) > 0
它会返回true或false,
True -> 键盘可见
False -> 键盘不可见
我们还可以使用WindowInsetListener
,就像这样
@Composable
fun keyboardAsState(): State<Boolean> {
val keyboardState = remember { mutableStateOf(false) }
val view = LocalView.current
LaunchedEffect(view) {
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
keyboardState.value = insets.isVisible(WindowInsetsCompat.Type.ime())
insets
}
}
return keyboardState
}
WindowCompat.setDecorFitsSystemWindows(window, false)
。 - mama@Composable
fun OnKeyboardClosedEffect(block: () -> Unit) {
val isKeyboardVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 130
var keyboardListenerHaBeenSet by remember { mutableStateOf(false) }
if (isKeyboardVisible || keyboardListenerHaBeenSet) {
if (!isKeyboardVisible) {
block()
keyboardListenerHaBeenSet = false // clear
}
keyboardListenerHaBeenSet = true
}
}
WindowInsets.isImeVisible
- k4dima