如何在 WindowManager.addView 中使用 Jetpack Compose 一、引出問題 Android 開發中,很常見的一個場景,通過 WindowManager.addView() 添加一個 View 到屏幕上。Android 最新的視圖框架 Jetpack Compose,如何 ...
如何在 WindowManager.addView 中使用 Jetpack Compose
一、引出問題
Android 開發中,很常見的一個場景,通過 WindowManager.addView()
添加一個 View 到屏幕上。Android 最新的視圖框架 Jetpack Compose,如何應用進來。這個被添加的 View 如何使用 Compose 編寫視圖呢?
二、探究問題
有的朋友肯定會馬上想到使用 ComposeView
作為橋梁。沒錯,WindowManager.addView
方法,就接收一個 View 類型的參數。那肯定是要藉助 ComposeView
了。但是,經過試驗,直接使用 ComposeView 是行不通的。
看代碼:
val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
val composeView: ComposeView = ComposeView(this).apply {
setContent {
Text(text = "I'm be added")
}
}
windowManager.addView(composeView, params)
上面代碼,編譯沒有問題,運行時會報錯:
FATAL EXCEPTION: main
Process: xxxxxxxx
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from androidx.compose.ui.platform.ComposeView{8285855 V.E...... ......I. 0,0-0,0}
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer(WindowRecomposer.android.kt:352)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer$default(WindowRecomposer.android.kt:325)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion$LifecycleAware$1.createRecomposer(WindowRecomposer.android.kt:168)
at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:224)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:300)
at androidx.compose.ui.platform.AbstractComposeView.resolveParentCompositionContext(ComposeView.android.kt:244)
at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:251)
at androidx.compose.ui.platform.AbstractComposeView.onAttachedToWindow(ComposeView.android.kt:283)
at android.view.View.dispatchAttachedToWindow(View.java:22065)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3553)
...
看這個錯誤信息:
應該是從 ComposeView 中沒有找到 ViewTreeLifecycleOwner, 其實很好理解。 View 的生命周期依賴於 ViewTreeLifecycleOwner, ComposeView 依賴於一個 ViewCompositonStrategy。核心問題是,ComposeView 需要一個 Lifecycle。
三、解決問題
有了思路自然就嘗試解決問題。
首先定義一個 LifecycleOwner ,
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.setViewTreeLifecycleOwner
import androidx.lifecycle.setViewTreeViewModelStoreOwner
import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryController
import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
class MyComposeViewLifecycleOwner:
LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
private val savedStateRegistryController = SavedStateRegistryController.create(this)
private val store = ViewModelStore()
override val lifecycle: Lifecycle
get() = lifecycleRegistry
override val savedStateRegistry: SavedStateRegistry
get() = savedStateRegistryController.savedStateRegistry
override val viewModelStore: ViewModelStore
get() = store
fun onCreate() {
savedStateRegistryController.performRestore(null)
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
fun onStart() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
}
fun onResume() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
fun onPause() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}
fun onStop() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
}
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?) {
decorView?.let {
it.setViewTreeViewModelStoreOwner(this)
it.setViewTreeLifecycleOwner(this)
it.setViewTreeSavedStateRegistryOwner(this)
} ?: return
}
}
再看看使用:
private var lifecycleOwner: MyComposeViewLifecycleOwner? = null
val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
val composeView: ComposeView = ComposeView(this).apply {
setContent {
Text(text = "I'm be added")
}
}
// 註意,在 調用 addView 之前:
lifecycleOwner = MyComposeViewLifecycleOwner().also {
it.onCreate() // 註意
it.attachToDecorView(composeView)
}
windowManager.addView(composeView, params)
windowManager.removeViewImmediate(composeView)
lifecycleOwner?.onDestroy()
lifecycleOwner = null
OK,再次運行。成功~
作者:SharpCJ 作者博客:http://joy99.cnblogs.com/ 本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接。