Android View与Compose互相调用实例探究

作者:氦客 时间:2021-06-11 09:07:29 

1. 前言

Compose 具有超强的兼容性,兼容现有的所有代码,Compose 能够与现有 View 体系并存,可实现渐进式替换。这就很有意义了,我们可以在现有项目中一小块一小块逐步地替换Compose,或者在旧项目中实现新的需求的时候,使用Compose

今天,我们就来演示一下,ComposeAndroid View怎么互相调用,以及在双层嵌套(原生View嵌套ComposeCompose中又嵌套原生View)的情况下,在最外层原生View中,怎么获取到Compose内部的原生View

2. Android传统View调用Compose

2.1 新建传统View体系的Android项目

新建项目的时候选择 Empty Activity

Android View与Compose互相调用实例探究

2.2 项目添加Compose配置

2.2.1 在android代码块添加

appbuild.config android代码块中添加

buildFeatures {
    compose true
}
composeOptions {
    kotlinCompilerExtensionVersion '1.1.1'
}

2.2.2 在dependencies中添加依赖

appbuild.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

Android 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 显示效果如下

Android View与Compose互相调用实例探究

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 显示效果如下所示

Android View与Compose互相调用实例探究

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

标签:Android,View,Compose
0
投稿

猜你喜欢

  • c#实现md5加密示例

    2023-04-19 16:37:11
  • android图片处理 让图片变成圆形

    2023-10-03 00:47:30
  • 浅谈Java封装、继承、多态特性

    2023-10-09 13:59:35
  • Spring Boot利用Docker快速部署项目的完整步骤

    2022-03-08 18:52:55
  • Java对xls文件进行读写操作示例代码

    2023-08-04 17:55:31
  • SpringData JPA中@OneToMany和@ManyToOne的用法详解

    2021-10-01 00:49:10
  • java实现图片转base64字符串 java实现base64字符串转图片

    2023-07-19 10:42:44
  • Java实现导出ZIP压缩包的方法

    2023-05-06 04:58:57
  • Javassist用法详解

    2023-06-26 17:11:54
  • SpringBoot 过滤器 Filter使用实例详解

    2021-11-08 13:46:00
  • java list集合排序按某一属性排序操作

    2021-06-24 03:33:25
  • 基于synchronized修饰静态和非静态方法

    2021-10-30 06:58:03
  • C#操作目录与文件的方法步骤

    2023-11-23 20:45:52
  • c# wpf如何附加依赖项属性

    2023-01-09 05:05:56
  • C#日期控件datetimepicker保存空值的三种方法

    2021-11-14 19:22:38
  • Android 中自定义Dialog样式的Activity点击空白处隐藏软键盘功能(dialog不消失)

    2022-03-10 12:15:21
  • 使用IDEA搭建ssm框架的详细图文教程

    2021-12-04 10:37:20
  • Java中实现可拖放图片剪裁入门教程

    2022-04-23 12:11:03
  • MyBatis多表关联查询的实现示例

    2023-07-06 06:26:49
  • 解决Eclipse的Servers视图中无法添加Tomcat6/Tomcat7的方法

    2023-03-30 22:22:27
  • asp之家 软件编程 m.aspxhome.com