Android View与Compose互相调用实例探究
作者:氦客 发布时间:2021-06-11 09:07:29
1. 前言
Compose
具有超强的兼容性,兼容现有的所有代码,Compose
能够与现有 View
体系并存,可实现渐进式替换。这就很有意义了,我们可以在现有项目中一小块一小块逐步地替换Compose
,或者在旧项目中实现新的需求的时候,使用Compose
。
今天,我们就来演示一下,Compose
和Android View
怎么互相调用,以及在双层嵌套(原生View
嵌套Compose
,Compose
中又嵌套原生View
)的情况下,在最外层原生View
中,怎么获取到Compose
内部的原生View
。
2. Android传统View调用Compose
2.1 新建传统View体系的Android项目
新建项目的时候选择 Empty Activity
2.2 项目添加Compose配置
2.2.1 在android代码块添加
在app
的build.config
android
代码块中添加
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.1.1'
}
2.2.2 在dependencies中添加依赖
在app
的build.config
dependencies
代码块中添加
dependencies {
//...省略...def compose_ui_version = '1.1.1'
implementation "androidx.compose.ui:ui:$compose_ui_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"implementation 'androidx.activity:activity-compose:1.3.1' //kotlin对应版本1.6.20
implementation 'androidx.compose.material:material:1.1.1'
}
2.3 定义Compose函数
在MainActivity.kt
中定义Compose
函数
@Composable
fun ComposeContent() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = "Hello world!")
}
}
2.4 修改xml文件
在activity_main.xml
中添加androidx.compose.ui.platform.ComposeView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
2.5 关联Compose函数
在MainActivity.kt
中,先通过findViewById
找到ComposeView
,然后通过composeView.setContent
将Android 传统View和Compose
建立关联。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val composeView : ComposeView = findViewById(R.id.compose_view)
composeView.setContent {
ComposeContent()
}
}
2.6 运行项目
可以发现界面显示如下,成功在传统View项目中调用了Compose
了
3. Compose中调用Android View
3.1 调用传统View的日历
3.1.1 使用AndroidView
在@Composable
内使用: androidx.compose.ui.viewinterop.AndroidView
,然后在factory
里面返回原生View
即可
@Composable
fun AndroidViewPage() {
AndroidView(factory = {
CalendarView(it)
}, modifier = Modifier.fillMaxWidth(), update = {
it.setOnDateChangeListener { view, year, month, day ->
Toast.makeText(view.context, "${year}年${month}月${day}日", Toast.LENGTH_SHORT).show()
}
})
}
3.1.2 显示效果如下
3.2 调用传统View的WebView
3.2.1 添加网络权限
首先需要在AndroidManifest.xml
中添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
3.2.2 首先要注册WebView的生命周期
@Composable
private fun rememberWebViewLifecycleObserver(webView: WebView): LifecycleEventObserver {
return remember(webView) {
LifecycleEventObserver { _, event ->
run {
when (event) {
Lifecycle.Event.ON_RESUME -> webView.onResume()
Lifecycle.Event.ON_PAUSE -> webView.onPause()
Lifecycle.Event.ON_DESTROY -> webView.destroy()
else -> Log.e("WebView", event.name)
}
}
}
}
}
3.2.3 创建有状态的WebView
创建有状态的WebView
,并注册生命周期
@Composable
fun rememberWebViewWIthLifecycle(): WebView {
val context = LocalContext.current
val webView = remember {
WebView(context)
}
val lifecycleObserver = rememberWebViewLifecycleObserver(webView)
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(lifecycle) {
lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycle.removeObserver(lifecycleObserver)
}
}
return webView
}
3.2.4 调用Android View
@Composable
fun WebViewPage() {
//创建有状态的WebView,并注册生命周期
val webView = rememberWebViewWIthLifecycle()
AndroidView(factory = {
webView
}, modifier = Modifier
.fillMaxSize() //宽高占满父布局
.background(Color.Red),
update = {webView ->
//设置支持JavaScript
val webSettings = webView.settings
webSettings.javaScriptEnabled = true
webView.loadUrl("https://www.baidu.com")
})
}
3.2.5 显示效果如下所示
4. 双层嵌套
获取AndroidView中的原生View id
有时候,我们会遇到这种情况,就是在原生项目了,页面中有部分使用了Compose,然后在Compose中又有部分组件使用了原生View,这种情况下,要如何取到AndroidView中的原生View id 呢 ?
4.1 在定义Xml中定义ComposeView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
4.2 关联Compose函数
在MainActivity.kt
中,先通过findViewById
找到ComposeView
,然后通过composeView.setContent
将Android 传统View和Compose
建立关联。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val composeView : ComposeView = findViewById(R.id.compose_view)
composeView.setContent {
ComposeContent()
}
}
@Composable
fun ComposeContent() {
//....
}
4.3 创建ids.xml定义原生view id
在resources/values
目录下创建ids.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="my_calendar_view" />
</resources>
4.4 实现ComposeContent
@Composable
fun ComposeContent() {
AndroidView(factory = {
//这里也可以通过 layoutInflater.inflate(R.layout.xxxxxx) 的方式返回原生View
val calendarView = CalendarView(it)
val keyboard = R.id.my_calendar_view
Log.i(TAG,"my_calendar_view id:$keyboard")
calendarView.id = keyboard
calendarView
}, modifier = Modifier.fillMaxWidth(), update = {
it.setOnDateChangeListener { view, year, month, day ->
Toast.makeText(view.context, "${year}年${month}月${day}日", Toast.LENGTH_SHORT).show()
}
})
}
4.5 在外层的原生代码处获取Compose中的原生View
在原生代码的地方,通过composeView.findViewById
查找id为my_calendar_view
的原生View
window?.decorView?.post {
val calendarViewId = R.id.my_calendar_view
Log.i(TAG,"my_calendar_view id ===>:$calendarViewId")
val calendarView = composeView.findViewById<CalendarView>(calendarViewId)
Log.i(TAG,"calendarView:$calendarView")
calendarView.setOnDateChangeListener { view, year, month, day ->
Toast.makeText(view.context, "!!!! ${year}年${month}月${day}日", Toast.LENGTH_SHORT).show()
}
}
注意这里的window?.decorView?.post
: 必须在页面加载完成后,才能查找到my_calendar_view
对应的原生View,如果直接在onCreate里面去查找,会发现composeView.findViewById<CalendarView>(calendarViewId)
返回的是null
4.6 运行项目
选择任意一个日期,可以发现弹出的toast是!!!! year年month月day日
,即原生的setOnDateChangeListener
覆盖了Compose
中的setOnDateChangeListener
监听,这样说明我们也在原生代码处,取到了Compose
内部的原生View了。
5. 本文源码下载
本文源码下载地址 : 传送门
来源:https://blog.csdn.net/EthanCo/article/details/128363313


猜你喜欢
- 一、采用Object.toString()toString方法是java.lang.Object对象的一个public方法。在java中任何
- spring拓展 定义自己的namespace1.查看源码认识spring是怎么加载xml配置的1.1 spring是怎么创建对象的? 查看
- 需求背景需求一:SpringMVC构建的微服务系统,数据库对日期的存储是Long类型的时间戳,前端之前是默认使用Long类型时间,现在前端框
- JMM与问题引入为啥先说JMM,因为CAS的实现类中维护的变量都被volatile修饰, 这个volatile 是遵循JMM规范(不是百分百
- Ben Evans是一名资深培训师兼顾问,他在演讲可视化垃圾回收中从基础谈起讨论了垃圾回收。以下是对其演讲的简短总结。基础当谈到释放不再使用
- 题目:有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。程序分析:请抓住分子与分母的变化规
- 一、Thymeleaf简介Thymeleaf是面向Web和独立环境的现代服务器Java模板引擎,能够处理HTML,XML,JavaScrip
- 我对java中lambda表达式的看法是相当纠结的:一个我这么想:lambda表达式降低了java程序的阅读体验。java程序一直不以表现力
- Java语言中,把异常分为两类:受检异常: 这类异常必须在throws子句中被显式抛出或者在方法内被捕获。例如,IOException异常或
- java 图片转base64字符串、base64字符串转图片,具体内容如下1. 图片转base64字符串: /** * base
- static :静态常量,静态方法,静态代码块静态变量: 静态变量属于类的,使用类名来访问,非静态变量是属于对象的,"必须&quo
- 在项目开发中,经常会碰到日期处理。比如查询中,可能会经常遇到按时间段查询,有时会默认取出一个月的数据。当我们提交数据时,会需要记录当前日期,
- c#创建圆形类,其中包括set,get方法using System;using System.Collections.Generic;usi
- 接收从控制台输入的数据可以使用Scanner类实现,Scanner类在一个名为util的包中需要在程序中导入这个包, 即在程序中添加impo
- 1、何为依赖冲突Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的。Maven的依赖机制会导致Jar包的冲突。举个例子,现在你的
- //初始化propertiesProperties pro = new Properties();try { &nbs
- 本文实例讲述了java内部类原理与用法。分享给大家供大家参考,具体如下:概念内部类:可以包含在另外一个类中的类外部类:包含内部类的类每个内部
- 有了上一节中得到的正则表达式,那么就可以用来构造 NFA 了。NFA 可以很容易的从正则表达式转换而来,也有助于理解正则表达式表示的模式。一
- 数组翻转的方法(java实现),数组翻转,就是将数组倒置,例如原数组为:{"a","b","
- 本文实例讲述了winform实现创建最前端窗体的方法。分享给大家供大家参考。具体实现方法如下:一、需求:1).需要这个窗体始终处于前端而且可