使用Jetpack Compose的InputMethodService-ComposeView原因:组合到不传播ViewTreeLifecycleOwner的View中 [英] InputMethodService with Jetpack Compose - ComposeView causes: Composed into the View which doesn't propagate ViewTreeLifecycleOwner
问题描述
您可以在 Github 上找到一个示例项目来重现该问题.
You can find a sample project to reproduce the issue on Github
我一直在尝试将Jetpack Compose用于键盘UI.最终,当我尝试通过InputMethodService给键盘充气时
I've been trying to use Jetpack Compose for a Keyboard UI. Ultimately, When I try to inflate the Keyboard via the InputMethodService
class IMEService : InputMethodService() {
override fun onCreateInputView(): View = KeyboardView(this)
}
使用此视图
class KeyboardView(context: Context) : FrameLayout(context) {
init {
val view = ComposeView(context).apply {
setContent {
Keyboard() //<- This is the actual compose UI function
}
}
addView(view)
}
}
或
class KeyboardView2 constructor(
context: Context,
) : AbstractComposeView(context) {
@Composable
override fun Content() {
Keyboard()
}
}
但是,当我尝试使用键盘时,出现以下错误
However, when I try to use the keyboard I get the following error
java.lang.IllegalStateException: Composed into the View which doesn't propagate ViewTreeLifecycleOwner!
at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.kt:599)
at android.view.View.dispatchAttachedToWindow(View.java:19676)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3458)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3465)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3465)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3465)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3465)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3465)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3465)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3465)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2126)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1817)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7779)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1031)
at android.view.Choreographer.doCallbacks(Choreographer.java:854)
at android.view.Choreographer.doFrame(Choreographer.java:789)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1016)
at android.os.Handler.handleCallback(Handler.java:914)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:227)
at android.app.ActivityThread.main(ActivityThread.java:7582)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:953)
官方文档状态
您必须将ComposeView附加到ViewTreeLifecycleOwner.ViewTreeLifecycleOwner允许在保留构图的同时重复附加和分离视图.ComponentActivity,FragmentActivity和AppCompatActivity都是实现ViewTreeLifecycleOwner的类的所有示例
You must attach the ComposeView to a ViewTreeLifecycleOwner. The ViewTreeLifecycleOwner allows the view to be attached and detached repeatedly while preserving the composition. ComponentActivity, FragmentActivity and AppCompatActivity are all examples of classes that implement ViewTreeLifecycleOwner
但是,我不能使用 ComponentActivity
, FragmentActivity
或 AppCompatActivity
来膨胀调用组合代码的View.我被困于实现 ViewTreeLifecycleOwner .我不知道该怎么办.
However, I cannot use ComponentActivity
, FragmentActivity
, or AppCompatActivity
to inflate the View which calls the compose code. I became stuck with implementing ViewTreeLifecycleOwner. I don't know how to do it.
如何使用 @Composable
函数作为输入法视图?
How can I use @Composable
functions as an Input Method View?
正如CommonsWare所建议的那样,我使用了 ViewTreeLifecycleOwner.set(...)
方法,还必须实现 ViewModelStoreOwner
和 SavedStateRegistryOwner
:>
As CommonsWare suggested I used the ViewTreeLifecycleOwner.set(...)
method and I also had to implement ViewModelStoreOwner
and SavedStateRegistryOwner
as well:
class IMEService : InputMethodService(), LifecycleOwner, ViewModelStoreOwner,
SavedStateRegistryOwner {
override fun onCreateInputView(): View {
val view = KeyboardView2(this)
ViewTreeLifecycleOwner.set(view, this)
ViewTreeViewModelStoreOwner.set(view, this)
ViewTreeSavedStateRegistryOwner.set(view, this)
return view
}
//Lifecycle Methods
private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
private fun handleLifecycleEvent(event: Lifecycle.Event) =
lifecycleRegistry.handleLifecycleEvent(event)
override fun onCreate() {
super.onCreate()
handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
override fun onDestroy() {
super.onDestroy()
handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
//ViewModelStore Methods
private val store = ViewModelStore()
override fun getViewModelStore(): ViewModelStore = store
//SaveStateRegestry Methods
private val savedStateRegistry = SavedStateRegistryController.create(this)
override fun getSavedStateRegistry(): SavedStateRegistry = savedStateRegistry.savedStateRegistry
}
现在我收到一个新错误
java.lang.IllegalStateException: You can consumeRestoredStateForKey only after super.onCreate of corresponding component
at androidx.savedstate.SavedStateRegistry.consumeRestoredStateForKey(SavedStateRegistry.java:77)
at androidx.compose.ui.platform.DisposableUiSavedStateRegistryKt.DisposableUiSavedStateRegistry(DisposableUiSavedStateRegistry.kt:69)
at androidx.compose.ui.platform.DisposableUiSavedStateRegistryKt.DisposableUiSavedStateRegistry(DisposableUiSavedStateRegistry.kt:44)
at androidx.compose.ui.platform.AndroidAmbientsKt.ProvideAndroidAmbients(AndroidAmbients.kt:162)
at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.kt:261)
[...]
这在某种程度上与生命周期事件传播有关,因为当我注释掉 onCreate
和 onDestroy
方法时,键盘可以正常打开,而不可见
This is somehow related to the lifecycle event propagation because when I comment out the onCreate
and onDestroy
methods the keyboard works opens without crashes, but the keyboard is not visible
推荐答案
在 ComponentActivity
中寻找类似的实现之后我终于想出了一个可行的解决方案:
After looking for similar implementations in ComponentActivity
I finally came up with a working solution:
class IMEService : InputMethodService(), LifecycleOwner, ViewModelStoreOwner,
SavedStateRegistryOwner {
override fun onCreateInputView(): View {
val view = ComposeKeyboardView(this)
ViewTreeLifecycleOwner.set(view, this)
ViewTreeViewModelStoreOwner.set(view, this)
ViewTreeSavedStateRegistryOwner.set(view, this)
return view
}
//Lifecylce Methods
private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
private fun handleLifecycleEvent(event: Lifecycle.Event) =
lifecycleRegistry.handleLifecycleEvent(event)
override fun onCreate() {
super.onCreate()
savedStateRegistry.performRestore(null)
handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
override fun onDestroy() {
super.onDestroy()
handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
//ViewModelStore Methods
private val store = ViewModelStore()
override fun getViewModelStore(): ViewModelStore = store
//SaveStateRegestry Methods
private val savedStateRegistry = SavedStateRegistryController.create(this)
override fun getSavedStateRegistry(): SavedStateRegistry = savedStateRegistry.savedStateRegistry
}
我不知道它是否是性能上最好的实现,但即使在较旧的设备上也能正常工作.赞赏改进想法
I don't know if it's the best implementation in terms of performance but it works fine even on older devices. Improvement ideas are appreciated
这篇关于使用Jetpack Compose的InputMethodService-ComposeView原因:组合到不传播ViewTreeLifecycleOwner的View中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!