如何在 WindowManager.addView 中使用 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,再次運行。成功~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章