Jetpack Compose,使用自定义 Lifecycle/ViewModelStore/SavedStateRegistry Owner 时不会触发重组

Gab*_*lie 5 android kotlin android-jetpack-compose compose-recomposition

我正在尝试创建一个覆盖层并将视图附加到 WindowManager,我已经通过创建一个实现 Lifecycle/ViewModelStore/SavedStateRegistry Owner 的类来设法做到这一点。\n但由于某种原因,每当我尝试显示对话框/更改时以任何方式使用 MutableState 或 MutableStateFlow 重组的 UI 都不会触发。

\n

有没有人尝试过做这样的事情并设法找到触发重组的解决方案?

\n

这就是我尝试创建和插入视图的方式:

\n
@SuppressLint("InflateParams")\nfun showOverlay() {\n    synchronized(lock) {\n        if (overlay != null || overlay?.parent != null) {\n            return\n        }\n        val viewModel = OverlayViewModel(context.applicationContext as Application)\n        var scope: CoroutineScope? = null\n        overlay = ComposeView(context).apply {\n            setContent {\n                Overlay(viewModel)\n            }\n        }\n        drawOverLifecycleOwner = DrawOverLifecycleOwner()\n        drawOverLifecycleOwner?.attachToDecorView(overlay)\n        val params =\n            WindowManager.LayoutParams(\n                WindowManager.LayoutParams.MATCH_PARENT,\n                WindowManager.LayoutParams.MATCH_PARENT,\n                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,\n                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,\n                PixelFormat.TRANSLUCENT\n            )\n        params.gravity = Gravity.TOP or Gravity.START\n        Log.i("ScreenLocker", "Inserting overlay")\n        ContextCompat.getMainExecutor(context).execute {\n            drawOverLifecycleOwner?.onCreate()\n            windowManager.addView(overlay, params)\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是自定义的 LifecycleOwner:

\n
import android.view.View\nimport androidx.lifecycle.*\nimport androidx.savedstate.SavedStateRegistry\nimport androidx.savedstate.SavedStateRegistryController\nimport androidx.savedstate.SavedStateRegistryOwner\nimport androidx.savedstate.ViewTreeSavedStateRegistryOwner\n\nclass DrawOverLifecycleOwner : LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {\n\n    fun onCreate() {\n        savedStateRegistryController.performRestore(null)\n        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)\n    }\n\n    fun onResume() {\n        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)\n    }\n\n    fun onPause() {\n        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)\n    }\n\n    fun onDestroy() {\n        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)\n        savedStateRegistryController.performSave(Bundle())\n        store.clear()\n    }\n\n    /**\n    Compose uses the Window's decor view to locate the\n    Lifecycle/ViewModel/SavedStateRegistry owners.\n    Therefore, we need to set this class as the "owner" for the decor view.\n     */\n    fun attachToDecorView(decorView: View?) {\n        if (decorView == null) return\n\n        ViewTreeLifecycleOwner.set(decorView, this)\n        ViewTreeViewModelStoreOwner.set(decorView, this)\n        ViewTreeSavedStateRegistryOwner.set(decorView, this)\n    }\n\n    // LifecycleOwner methods\n    private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)\n\n    override fun getLifecycle(): Lifecycle = lifecycleRegistry\n\n    // ViewModelStore methods\n    private val store = ViewModelStore()\n    override fun getViewModelStore(): ViewModelStore = store\n\n    // SavedStateRegistry methods\n    private val savedStateRegistryController = SavedStateRegistryController.create(this)\n    override val savedStateRegistry: SavedStateRegistry\n        get() = savedStateRegistryController.savedStateRegistry\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是叠加层:

\n
@Composable\nfun Overlay(viewModel: OverlayViewModel = viewModel()) {\n    val showDialog = remember { MutableStateFlow(false) }\n    AppTheme {\n        Logger.info("Recomposition triggered")\n        ConstraintLayout(\n            modifier = Modifier\n                .fillMaxSize()\n                .background(Color.Black)\n        ) {\n            val (dialog) = createRefs()\n            Box(modifier = Modifier.fillMaxSize()) {\n                Text(\n                    modifier = Modifier\n                        .align(Alignment.TopEnd)\n                        .padding(top = 25.dp, end = 15.dp),\n                    text = "\xc2\xa9 2022",\n                    color = Color.White\n                )\n                Column(\n                    modifier = Modifier.align(Alignment.Center),\n                    horizontalAlignment = Alignment.CenterHorizontally\n                ) {\n                    Image(\n                        modifier = Modifier\n                            .fillMaxWidth()\n                            .padding(15.dp),\n                        painter = painterResource(R.drawable.logo),\n                        contentDescription = "Logo",\n                        contentScale = ContentScale.Fit,\n                    )\n                    Text(\n                        modifier = Modifier.padding(bottom = 25.dp),\n                        text = "Overlay Text",\n                        textAlign = TextAlign.Center,\n                        color = Color.White,\n                        fontWeight = FontWeight.Bold,\n                        style = MaterialTheme.typography.headlineMedium\n                    )\n                    LazyRow {\n                        items(LockService.whitelistedApps) { app ->\n                            Button(\n                                onClick = { viewModel.launchApp(app) },\n                                colors = ButtonDefaults.buttonColors(\n                                    containerColor = Color.Transparent\n                                )\n                            ) {\n                                Column(horizontalAlignment = Alignment.CenterHorizontally) {\n                                    viewModel.getPackageIcon(app)?.apply {\n                                        Image(\n                                            this.asImageBitmap(),\n                                            contentDescription = "App Icon"\n                                        )\n                                    }\n                                    Text(\n                                        text = viewModel.getPackageLabel(app),\n                                        color = Color.White\n                                    )\n                                }\n                            }\n                        }\n                    }\n                }\n                IconButton(\n                    modifier = Modifier\n                        .align(Alignment.BottomStart)\n                        .padding(bottom = 15.dp, start = 15.dp),\n                    onClick = {\n                        Logger.info("Trying to show dialog, showDialogValue = ${showDialog.value}")\n                        showDialog.tryEmit(true)\n                    }\n                ) {\n                    Icon(\n                        imageVector = Icons.Default.Info,\n                        contentDescription = "Info Dialog Icon",\n                        tint = Color.White\n                    )\n                }\n            }\n            if (showDialog.collectAsState().value) {\n                InfoDialog(showDialog, Modifier.constrainAs(dialog) {\n                    top.linkTo(parent.top)\n                    bottom.linkTo(parent.bottom)\n                    start.linkTo(parent.start)\n                    end.linkTo(parent.end)\n                })\n            }\n        }\n    }\n}\n\n@Preview\n@Composable\nfun OverlayPreview() {\n    AppTheme {\n        Overlay(OverlayViewModel(Application()))\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

小智 5

嘿,我发现我的实现中存在问题,可能与您的问题相同。

当你强制将lifecycleOwner设置为ComposeView时,你是否也设置了ON_START事件?在我的系统中,当我完成可组合项开始响应即将到来的新状态时,它丢失了。

        val viewModelStore = ViewModelStore()
        lifecycleOwner.performRestore(null)
        lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
        lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START)
        ViewTreeLifecycleOwner.set(composeView, lifecycleOwner)
        ViewTreeViewModelStoreOwner.set(composeView) { viewModelStore }
        it.setViewTreeSavedStateRegistryOwner(lifecycleOwner)
Run Code Online (Sandbox Code Playgroud)