如何在 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,再次运行。成功~

如何在 WindowManager.addView 中使用 Jetpack Compose的更多相关文章

  1. Jetpack Compose学习(9)——Compose中的列表控件(LazyRow和LazyColumn)

    原文:Jetpack Compose学习(9)--Compose中的列表控件(LazyRow和LazyColumn) - Stars-One的杂货小窝 经过前面的学习,大致上已掌握了compose的基 ...

  2. Jetpack Compose和View的互操作性

    Jetpack Compose Interoperability Compose风这么大, 对于已有项目使用新技术, 难免会担心兼容性. 对于Compose来说, 至少和View的结合是无缝的. (目 ...

  3. Jetpack Compose What and Why, 6个问题

    Jetpack Compose What and Why, 6个问题 1.这个技术出现的背景, 初衷, 要达到什么样的目标或是要解决什么样的问题. Jetpack Compose是什么? 它是一个声明 ...

  4. Jetpack Compose学习(1)——从登录页开始入门

    原文地址:Jetpack Compose学习(1)--从登录页开始入门 | Stars-One的杂货小窝 Jetpack Compose UI在前几天出了1.0正式版,之前一直还在观望,终于是出了正式 ...

  5. Jetpack Compose 1.0 终于要投入使用了!

    前言 Jetpack Compose 是用于构建原生界面的「新款 Android 工具包」.2021 Google IO 大会上,Google宣布:「Jetpack Compose 1.0 即将面世」 ...

  6. Android Kotlin Jetpack Compose UI框架 完全解析

    前言 Q1的时候公司列了个培训计划,部分人作为讲师要上报培训课题.那时候刚从好几个Android项目里抽离出来,正好看到Jetpack发布了新玩意儿--Compose,我被它的快速实时打包给吸引住了, ...

  7. Android全新UI编程 - Jetpack Compose 超详细教程

    1. 简介 Jetpack Compose是在2019Google i/O大会上发布的新的库.Compose库是用响应式编程的方式对View进行构建,可以用更少更直观的代码,更强大的功能,能提高开发速 ...

  8. Jetpack Compose学习(2)——文本(Text)的使用

    原文: Jetpack Compose学习(2)--文本(Text)的使用 | Stars-One的杂货小窝 对于开发来说,文字最为基础的组件,我们先从这两个使用开始吧 本篇涉及到Kotlin和DSL ...

  9. Jetpack Compose之隐藏Scaffold的BottomNavigation

    做主页导航时会用到底部导航栏,Jetpack Compose提供了基础槽位的布局Scaffold,使用Scaffold可以构建底部导航栏,例如: @Composable fun Greeting(vm ...

  10. Jetpack Compose学习(3)——图标(Icon) 按钮(Button) 输入框(TextField) 的使用

    原文地址: Jetpack Compose学习(3)--图标(Icon) 按钮(Button) 输入框(TextField) 的使用 | Stars-One的杂货小窝 本篇分别对常用的组件:图标(Ic ...

随机推荐

  1. [转帖]/etc/profile和/etc/environment的区别

    时间  2019-11-07 标签 profile environment 区别 繁體版 原文   https://my.oschina.net/u/2885925/blog/2989579 /etc ...

  2. Docker与虚拟化技术浅析第一弹之docker与Kubernetes

    1 前言 Docker是一个开源的引擎,可以轻松地为任何应用创建一个轻量级的. 可移植的.自给自足的容器.开发者在笔记本电脑上编译测试通过的容器可以批量地在生产环境中部署,包括VMs (虚拟机).ba ...

  3. Java单元测试浅析(JUnit+Mockito)

    作者:京东物流 秦彪 1. 什么是单元测试 (1)单元测试环节: 测试过程按照阶段划分分为:单元测试.集成测试.系统测试.验收测试等.相关含义如下: 1)       单元测试: 针对计算机程序模块进 ...

  4. kettle(docker版)系列文章02---hello world

    目标:将mysql库中表的数据定时转到另外一个表中 1.在mysql中新建目标表test_kettle,有字段id,productName,createtime 2.连接mysql数据库 DB连接-- ...

  5. .NET 使用Camunda快速入门

    简介参考:https://www.cnblogs.com/lvdeyinBlog/p/16095603.html 一.工作流介绍 1. 什么是工作流 工作流(Workflow),是对工作流程及其各操作 ...

  6. TienChin 验证码响应结果分析&验证码生成接口分析

    验证码响应结果分析 首先从前端开始进行分析,进入到登录页面,打开开发者工具(f12),找到 network,f5 刷新一下页面,然后,筛选一下,筛选内容为 Fetch/XHR: 你会发现列表中有两项内 ...

  7. 设计模式学习-使用go实现备忘录模式

    备忘录模式 定义 优点 缺点 适用范围 代码实现 参考 备忘录模式 定义 备忘录( Memento ):在不破坏封装性的前提下,获取一个对象的内部状态,并在该对象之处保存该状态.这样以后就可将该对象恢 ...

  8. MetaGPT( The Multi-Agent Framework):颠覆AI开发的革命性多智能体元编程框架

    "MetaGPT( The Multi-Agent Framework):颠覆AI开发的革命性多智能体元编程框架" 一个多智能体元编程框架,给定一行需求,它可以返回产品文档.架构设 ...

  9. 做知识图谱遇到的环境问题合集【spacy、gensim、keras_contrib等】

    1.python:spacy.gensim库的安装遇到问题及bug处理 见: python:spacy.gensim库的安装遇到问题及bug处理_汀.的博客-CSDN博客1.spacySpaCy最新版 ...

  10. C/C++ Qt 常用数据结构

    Qt 是一个跨平台的图形化类库,常用数据结构就是对C++ STL的二次封装,使其更加易用,如下是经常会用到的一些数据结构和算法,其中包括了QString,QList,QLinkedList,QVect ...