您可以找到一个在https://github.com/THEAccess/temp_compose_ime上重现问题的示例项目。
我一直在尝试为键盘UI使用。最终,当我试图通过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()
}
}
但是,当我尝试使用键盘时,会出现以下错误
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的类的示例。
但是,我不能使用ComponentActivity
、FragmentActivity
或AppCompatActivity
来膨胀调用组成代码的视图。我被困在实现ViewTreeLifecycleOwner上了。我不知道怎么做。
如何使用@Composable
函数作为输入方法视图?
编辑:,正如CommonsWare建议的那样,我使用了ViewTreeLifecycleOwner.set(...)
方法,我还必须实现ViewModelStoreOwner
和SavedStateRegistryOwner
:
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
方法时,键盘就会在没有崩溃的情况下打开,但是键盘是不可见的。
发布于 2021-01-17 11:34:44
在ComponentActivity
中寻找类似的实现之后,我终于想出了一个可行的解决方案:
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
}
我不知道它在性能上是否是最好的实现,但即使在较旧的设备上,它也能正常工作。改进意见受到赞赏。
发布于 2021-04-05 19:34:35
我的答案很大程度上是基于Yannick的回答和其他相关来源,所以他们的功劳是很大的。
本质上,编写androidx.lifecycle
包需要三个“所有者”类:LifecycleOwner
、ViewModelStoreOwner
和SavedStateRegistryOwner
。AppCompatActivity
和Fragment
已经实现了这些接口,因此为它们设置一个ComposeView
就可以了。
但是,在构建IME应用程序时,您无法访问某个活动或片段。
因此,您必须实现自己的“所有者”类,这些类与您从InputMethodService
获得的生命周期回调绑定在一起。以下是如何做到这一点:
class KeyboardViewLifecycleOwner :
LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
fun onCreate() {
savedStateRegistryController.performRestore(null)
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
fun onResume() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
fun onPause() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}
fun onDestroy() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
store.clear()
}
/**
Compose uses the Window's decor view to locate the
Lifecycle/ViewModel/SavedStateRegistry owners.
Therefore, we need to set this class as the "owner" for the decor view.
*/
fun attachToDecorView(decorView: View?) {
if (decorView == null) return
ViewTreeLifecycleOwner.set(decorView, this)
ViewTreeViewModelStoreOwner.set(decorView, this)
ViewTreeSavedStateRegistryOwner.set(decorView, this)
}
// LifecycleOwner methods
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
override fun getLifecycle(): Lifecycle = lifecycleRegistry
// ViewModelStore methods
private val store = ViewModelStore()
override fun getViewModelStore(): ViewModelStore = store
// SavedStateRegistry methods
private val savedStateRegistryController = SavedStateRegistryController.create(this)
override fun getSavedStateRegistry(): SavedStateRegistry =
savedStateRegistryController.savedStateRegistry
}
InputMethodService
的类中,重写回调并将这些消息中继到我们在步骤1中定义的类的实例:class MyKeyboardService : InputMethodService() {
private val keyboardViewLifecycleOwner = KeyboardViewLifecycleOwner()
override fun onCreate() {
super.onCreate()
keyboardViewLifecycleOwner.onCreate()
}
override fun onCreateInputView(): View {
//Compose uses the decor view to locate the "owner" instances
keyboardViewLifecycleOwner.attachToDecorView(
window?.window?.decorView
)
return MyComposeView(this)
}
override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
keyboardViewLifecycleOwner.onResume()
}
override fun onFinishInputView(finishingInput: Boolean) {
keyboardViewLifecycleOwner.onPause()
}
override fun onDestroy() {
super.onDestroy()
keyboardViewLifecycleOwner.onDestroy()
}
}
通过这样做,现在Compose就有了一个合适的生命周期,可以用来确定何时执行重组等等。
资料来源:
发布于 2022-08-01 10:03:23
基于@Yannick的回答,我添加了对"androidx.savedstate:1.2.0“的依赖,并修改了一些代码。
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package io.simsim.demo.fetal
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.annotation.CallSuper
import androidx.lifecycle.*
import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryController
import androidx.savedstate.SavedStateRegistryOwner
abstract class BaseService :
Service(),
LifecycleOwner,
ViewModelStoreOwner,
SavedStateRegistryOwner {
private val _lifecycleRegistry: LifecycleRegistry by lazy { LifecycleRegistry(this) }
private val _store by lazy { ViewModelStore() }
override fun getLifecycle(): Lifecycle = _lifecycleRegistry
override fun getViewModelStore(): ViewModelStore = _store
override val savedStateRegistry: SavedStateRegistry = SavedStateRegistryController.create(this).savedStateRegistry
private fun handleLifecycleEvent(event: Lifecycle.Event) =
_lifecycleRegistry.handleLifecycleEvent(event)
@CallSuper
override fun onCreate() {
super.onCreate()
// You must call performAttach() before calling performRestore(Bundle)
savedStateRegistry.performAttach(lifecycle)
savedStateRegistry.performRestore(null)
handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
@CallSuper
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
stopSelf()
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
@CallSuper
override fun onDestroy() {
super.onDestroy()
handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
}
//像这样使用ComposeView
ComposeView(this).also {
it.setContent(content)
ViewTreeLifecycleOwner.set(it, this)
ViewTreeViewModelStoreOwner.set(it, this)
it.setViewTreeSavedStateRegistryOwner(this)
}
https://stackoverflow.com/questions/65755763
复制相似问题