经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Android View与Compose互相调用实例探究
来源:jb51  时间:2023/2/1 9:35:41  对本文有异议

1. 前言

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

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

2. Android传统View调用Compose

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

新建项目的时候选择 Empty Activity

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函数

  1. @Composable
  2. fun ComposeContent() {
  3. Box(
  4. modifier = Modifier.fillMaxSize(),
  5. contentAlignment = Alignment.Center
  6. ) {
  7. Text(text = "Hello world!")
  8. }
  9. }

2.4 修改xml文件

activity_main.xml中添加androidx.compose.ui.platform.ComposeView

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. android:orientation="vertical"
  8. tools:context=".MainActivity">
  9. <androidx.compose.ui.platform.ComposeView
  10. android:id="@+id/compose_view"
  11. android:layout_width="match_parent"
  12. android:layout_height="match_parent" />
  13. </LinearLayout>

2.5 关联Compose函数

MainActivity.kt中,先通过findViewById找到ComposeView,然后通过composeView.setContent将Android 传统View和Compose建立关联。

  1. override fun onCreate(savedInstanceState: Bundle?) {
  2. super.onCreate(savedInstanceState)
  3. setContentView(R.layout.activity_main)
  4. val composeView : ComposeView = findViewById(R.id.compose_view)
  5. composeView.setContent {
  6. ComposeContent()
  7. }
  8. }

2.6 运行项目

可以发现界面显示如下,成功在传统View项目中调用了Compose

3. Compose中调用Android View

3.1 调用传统View的日历

3.1.1 使用AndroidView

@Composable内使用: androidx.compose.ui.viewinterop.AndroidView,然后在factory里面返回原生View即可

  1. @Composable
  2. fun AndroidViewPage() {
  3. AndroidView(factory = {
  4. CalendarView(it)
  5. }, modifier = Modifier.fillMaxWidth(), update = {
  6. it.setOnDateChangeListener { view, year, month, day ->
  7. Toast.makeText(view.context, "${year}年${month}月${day}日", Toast.LENGTH_SHORT).show()
  8. }
  9. })
  10. }

3.1.2 显示效果如下

3.2 调用传统View的WebView

3.2.1 添加网络权限

首先需要在AndroidManifest.xml中添加网络权限

  1. <uses-permission android:name="android.permission.INTERNET" />

3.2.2 首先要注册WebView的生命周期

  1. @Composable
  2. private fun rememberWebViewLifecycleObserver(webView: WebView): LifecycleEventObserver {
  3. return remember(webView) {
  4. LifecycleEventObserver { _, event ->
  5. run {
  6. when (event) {
  7. Lifecycle.Event.ON_RESUME -> webView.onResume()
  8. Lifecycle.Event.ON_PAUSE -> webView.onPause()
  9. Lifecycle.Event.ON_DESTROY -> webView.destroy()
  10. else -> Log.e("WebView", event.name)
  11. }
  12. }
  13. }
  14. }
  15. }

3.2.3 创建有状态的WebView

创建有状态的WebView,并注册生命周期

  1. @Composable
  2. fun rememberWebViewWIthLifecycle(): WebView {
  3. val context = LocalContext.current
  4. val webView = remember {
  5. WebView(context)
  6. }
  7. val lifecycleObserver = rememberWebViewLifecycleObserver(webView)
  8. val lifecycle = LocalLifecycleOwner.current.lifecycle
  9. DisposableEffect(lifecycle) {
  10. lifecycle.addObserver(lifecycleObserver)
  11. onDispose {
  12. lifecycle.removeObserver(lifecycleObserver)
  13. }
  14. }
  15. return webView
  16. }

3.2.4 调用Android View

  1. @Composable
  2. fun WebViewPage() {
  3. //创建有状态的WebView,并注册生命周期
  4. val webView = rememberWebViewWIthLifecycle()
  5. AndroidView(factory = {
  6. webView
  7. }, modifier = Modifier
  8. .fillMaxSize() //宽高占满父布局
  9. .background(Color.Red),
  10. update = {webView ->
  11. //设置支持JavaScript
  12. val webSettings = webView.settings
  13. webSettings.javaScriptEnabled = true
  14. webView.loadUrl("https://www.baidu.com")
  15. })
  16. }

3.2.5 显示效果如下所示

4. 双层嵌套

获取AndroidView中的原生View id

有时候,我们会遇到这种情况,就是在原生项目了,页面中有部分使用了Compose,然后在Compose中又有部分组件使用了原生View,这种情况下,要如何取到AndroidView中的原生View id 呢 ?

4.1 在定义Xml中定义ComposeView

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:app="http://schemas.android.com/apk/res-auto"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:orientation="vertical"
  7. tools:context=".MainActivity">
  8. <androidx.compose.ui.platform.ComposeView
  9. android:id="@+id/compose_view"
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent" />
  12. </LinearLayout>

4.2 关联Compose函数

MainActivity.kt中,先通过findViewById找到ComposeView,然后通过composeView.setContent将Android 传统View和Compose建立关联。

  1. override fun onCreate(savedInstanceState: Bundle?) {
  2. super.onCreate(savedInstanceState)
  3. setContentView(R.layout.activity_main)
  4. val composeView : ComposeView = findViewById(R.id.compose_view)
  5. composeView.setContent {
  6. ComposeContent()
  7. }
  8. }
  9. @Composable
  10. fun ComposeContent() {
  11. //....
  12. }

4.3 创建ids.xml定义原生view id

resources/values目录下创建ids.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <item type="id" name="my_calendar_view" />
  4. </resources>

4.4 实现ComposeContent

  1. @Composable
  2. fun ComposeContent() {
  3. AndroidView(factory = {
  4. //这里也可以通过 layoutInflater.inflate(R.layout.xxxxxx) 的方式返回原生View
  5. val calendarView = CalendarView(it)
  6. val keyboard = R.id.my_calendar_view
  7. Log.i(TAG,"my_calendar_view id:$keyboard")
  8. calendarView.id = keyboard
  9. calendarView
  10. }, modifier = Modifier.fillMaxWidth(), update = {
  11. it.setOnDateChangeListener { view, year, month, day ->
  12. Toast.makeText(view.context, "${year}年${month}月${day}日", Toast.LENGTH_SHORT).show()
  13. }
  14. })
  15. }

4.5 在外层的原生代码处获取Compose中的原生View

在原生代码的地方,通过composeView.findViewById查找id为my_calendar_view的原生View

  1. window?.decorView?.post {
  2. val calendarViewId = R.id.my_calendar_view
  3. Log.i(TAG,"my_calendar_view id ===>:$calendarViewId")
  4. val calendarView = composeView.findViewById<CalendarView>(calendarViewId)
  5. Log.i(TAG,"calendarView:$calendarView")
  6. calendarView.setOnDateChangeListener { view, year, month, day ->
  7. Toast.makeText(view.context, "!!!! ${year}年${month}月${day}日", Toast.LENGTH_SHORT).show()
  8. }
  9. }

注意这里的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. 本文源码下载

本文源码下载地址 : 传送门

到此这篇关于Android View与Compose互相调用实例探究的文章就介绍到这了,更多相关Android View与Compose 内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号