如何在 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,再次运行。成功~
如何在 WindowManager.addView 中使用 Jetpack Compose的更多相关文章
- Jetpack Compose学习(9)——Compose中的列表控件(LazyRow和LazyColumn)
原文:Jetpack Compose学习(9)--Compose中的列表控件(LazyRow和LazyColumn) - Stars-One的杂货小窝 经过前面的学习,大致上已掌握了compose的基 ...
- Jetpack Compose和View的互操作性
Jetpack Compose Interoperability Compose风这么大, 对于已有项目使用新技术, 难免会担心兼容性. 对于Compose来说, 至少和View的结合是无缝的. (目 ...
- Jetpack Compose What and Why, 6个问题
Jetpack Compose What and Why, 6个问题 1.这个技术出现的背景, 初衷, 要达到什么样的目标或是要解决什么样的问题. Jetpack Compose是什么? 它是一个声明 ...
- Jetpack Compose学习(1)——从登录页开始入门
原文地址:Jetpack Compose学习(1)--从登录页开始入门 | Stars-One的杂货小窝 Jetpack Compose UI在前几天出了1.0正式版,之前一直还在观望,终于是出了正式 ...
- Jetpack Compose 1.0 终于要投入使用了!
前言 Jetpack Compose 是用于构建原生界面的「新款 Android 工具包」.2021 Google IO 大会上,Google宣布:「Jetpack Compose 1.0 即将面世」 ...
- Android Kotlin Jetpack Compose UI框架 完全解析
前言 Q1的时候公司列了个培训计划,部分人作为讲师要上报培训课题.那时候刚从好几个Android项目里抽离出来,正好看到Jetpack发布了新玩意儿--Compose,我被它的快速实时打包给吸引住了, ...
- Android全新UI编程 - Jetpack Compose 超详细教程
1. 简介 Jetpack Compose是在2019Google i/O大会上发布的新的库.Compose库是用响应式编程的方式对View进行构建,可以用更少更直观的代码,更强大的功能,能提高开发速 ...
- Jetpack Compose学习(2)——文本(Text)的使用
原文: Jetpack Compose学习(2)--文本(Text)的使用 | Stars-One的杂货小窝 对于开发来说,文字最为基础的组件,我们先从这两个使用开始吧 本篇涉及到Kotlin和DSL ...
- Jetpack Compose之隐藏Scaffold的BottomNavigation
做主页导航时会用到底部导航栏,Jetpack Compose提供了基础槽位的布局Scaffold,使用Scaffold可以构建底部导航栏,例如: @Composable fun Greeting(vm ...
- Jetpack Compose学习(3)——图标(Icon) 按钮(Button) 输入框(TextField) 的使用
原文地址: Jetpack Compose学习(3)--图标(Icon) 按钮(Button) 输入框(TextField) 的使用 | Stars-One的杂货小窝 本篇分别对常用的组件:图标(Ic ...
随机推荐
- [转帖]SPEC2006
安装步骤 # Ubuntu16.04 # 注意安装gFortran . ./install.sh . ./shrc 一般情况下经过以上步骤即可安装完毕,进行使用,注意需要执行shrc设置完环境变量以后 ...
- 简单进行Springboot Beans归属模块单元的统计分析方法
简单进行Springboot Beans归属模块单元的统计分析方法 背景 基于Springboot的产品变的复杂之后 启动速度会越来越慢. 公司同事得出一个结论. beans 数量过多会导致启动速度逐 ...
- 查看java所有的线程信息
最近一直有一个困惑, 不知道如何查看所有的java的线程信息. 今天看blog时发现了一个简单方法 ps -Tp $pid 就可以了 也可以使用 ps- Lfp $pid的方式 这里简单写一下统计方法 ...
- 【JS 逆向百例】HN某服务网登录逆向,验证码形同虚设
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:某政务服务 ...
- vue脚手架创建与环境安装
1.安装 Node.jsDownload | Node.js 在这里下载的是最新版,如果要安装以前的版本,页面往下拉找到 Previous Releases-Donloads-下载msi文件. ...
- 快速上手NPM包管理
NPM官网 前提 安装nodejs 检测安装成功的命令 node -v 显示版本号即为安装成功 快速上手教程 第一步注册NPM账号官网在上面 第二步骤随便找个地方新建一个文件夹 然后这个文件夹我们用C ...
- Postman 简单使用随笔记
1.要先使用Postman post请求,返回token,否则提示未登陆系统,下图为发送请求后的接口返回的信息 2.为方便,每次在访问接口时都要访问权限,所以将其作为局部的环境变量,设置如下: 3.要 ...
- Softmax偏导及BP过程的推导
Softmax求导 其实BP过程在pytorch中可以自动进行,这里进行推导只是强迫症 A Apart证明softmax求导和softmax的BP过程 本来像手打公式的,想想还是算了,引用部分给出la ...
- Java-将文本(字符串)转化成二进制字符
今天在测试MySQL的Blob相关类型时,这种一般存放的是二进制文本,所以就想插入二进制文本. package com.aaa.dao; public class aaa { public stati ...
- Python脚本的输入输出
一.必备知识回顾和补充 1. Hello world回顾 1.输出文本,使用print函数输出文本. 2.让用户输入名字,然后输出带名字的问候语.使用input函数获取用户的输入,使用变量保存输入值. ...