如何在 jetpack compose 中检测键盘的打开和关闭?

new*_*our 39 android-jetpack-compose

我在撰写中发现的唯一方法是使用accompanist-insetsand 删除窗口插图。这会导致我的应用程序布局出现其他问题。

Android 方式似乎是这样的,我可以将其传递到我的撰写应用程序中并采取相应的操作。

jetpack compose 还有其他方法吗?

uji*_*zin 105

更新

有了新的WindowInsetsAPI,事情变得更容易

首先,要返回正确的值,您需要设置:

WindowCompat.setDecorFitsSystemWindows(window, false)
Run Code Online (Sandbox Code Playgroud)

然后使用键盘作为状态:

@Composable
fun keyboardAsState(): State<Boolean> {
    val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
    return rememberUpdatedState(isImeVisible)
}
Run Code Online (Sandbox Code Playgroud)

使用示例:

val isKeyboardOpen by keyboardAsState() // true or false
Run Code Online (Sandbox Code Playgroud)

ps:我尝试使用 WindowInsets.isImeVisible,但它在第一次调用中返回 true。


没有实验性 API

如果你想要声明,我找到了这个解决方案:

WindowCompat.setDecorFitsSystemWindows(window, false)
Run Code Online (Sandbox Code Playgroud)

要检测/检查该值,您只需要这样:

@Composable
fun keyboardAsState(): State<Boolean> {
    val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
    return rememberUpdatedState(isImeVisible)
}
Run Code Online (Sandbox Code Playgroud)

  • 非常好的解决方案。谢谢。 (5认同)
  • 做得非常好! (5认同)

Sty*_*pox 27

这是一个解决方案,用于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)
    }
}
Run Code Online (Sandbox Code Playgroud)

对我来说,其他解决方案效果不佳:键盘将始终关闭。

  • OnGlobalLayoutListener基于 - 的答案中,所使用的公式似乎没有发挥应有的作用,并且使用了旧的 API 。
  • WindowInsetListener基于 - 的答案中,由于view不是根视图,因此不会对其应用窗口插入。我尝试替换viewview.rootView,尽管键盘检测代码可以工作,但传递根视图来setOnApplyWindowInsetsListener替换组件设置的任何侦听器,这显然是不需要的。


Atr*_*thi 6

要在不更改 Activity 中的设置的情况下处理 Jetpack Compose 中的键盘打开和关闭行为WindowCompat.setDecorFitsSystemWindows(window, false),尤其是在使用包含 Fragments 和 Composables 的单个 Activity 架构时,您可以使用以下方法:

@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)
}
Run Code Online (Sandbox Code Playgroud)

此代码片段提供了一个可组合函数keyboardAsState,允许您观察并响应键盘可见状态的变化。它通过利用DisposableEffectViewTreeObserver.OnPreDrawListener来检测键盘何时打开或关闭来实现这一点。

要使用此函数,只需在val isKeyboardOpen by keyboardAsState()需要跟踪键盘状态的可组合项中访问其值即可。它将返回State<Boolean>反映键盘当前是否可见的 a 。


new*_*our 2

我找到了一种使用 Android 的方法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()
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)