Android View與Compose互相調(diào)用實例探究
1. 前言
Compose 具有超強的兼容性,兼容現(xiàn)有的所有代碼,Compose 能夠與現(xiàn)有 View 體系并存,可實現(xiàn)漸進(jìn)式替換。這就很有意義了,我們可以在現(xiàn)有項目中一小塊一小塊逐步地替換Compose,或者在舊項目中實現(xiàn)新的需求的時候,使用Compose。
今天,我們就來演示一下,Compose和Android View怎么互相調(diào)用,以及在雙層嵌套(原生View嵌套Compose,Compose中又嵌套原生View)的情況下,在最外層原生View中,怎么獲取到Compose內(nèi)部的原生View。
2. Android傳統(tǒng)View調(diào)用Compose
2.1 新建傳統(tǒng)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對應(yīng)版本1.6.20
implementation 'androidx.compose.material:material:1.1.1'
}
2.3 定義Compose函數(shù)
在MainActivity.kt中定義Compose函數(shù)
@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 關(guān)聯(lián)Compose函數(shù)
在MainActivity.kt中,先通過findViewById找到ComposeView,然后通過composeView.setContent將Android 傳統(tǒng)View和Compose建立關(guān)聯(lián)。
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 運行項目
可以發(fā)現(xiàn)界面顯示如下,成功在傳統(tǒng)View項目中調(diào)用了Compose了

3. Compose中調(diào)用Android View
3.1 調(diào)用傳統(tǒng)View的日歷
3.1.1 使用AndroidView
在@Composable內(nèi)使用: 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 調(diào)用傳統(tǒng)View的WebView
3.2.1 添加網(wǎng)絡(luò)權(quán)限
首先需要在AndroidManifest.xml中添加網(wǎng)絡(luò)權(quán)限
<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 創(chuàng)建有狀態(tài)的WebView
創(chuàng)建有狀態(tài)的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 調(diào)用Android View
@Composable
fun WebViewPage() {
//創(chuàng)建有狀態(tài)的WebView,并注冊生命周期
val webView = rememberWebViewWIthLifecycle()
AndroidView(factory = {
webView
}, modifier = Modifier
.fillMaxSize() //寬高占滿父布局
.background(Color.Red),
update = {webView ->
//設(shè)置支持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 關(guān)聯(lián)Compose函數(shù)
在MainActivity.kt中,先通過findViewById找到ComposeView,然后通過composeView.setContent將Android 傳統(tǒng)View和Compose建立關(guān)聯(lián)。
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 創(chuàng)建ids.xml定義原生view id
在resources/values目錄下創(chuàng)建ids.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="my_calendar_view" />
</resources>
4.4 實現(xiàn)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對應(yīng)的原生View,如果直接在onCreate里面去查找,會發(fā)現(xiàn)composeView.findViewById<CalendarView>(calendarViewId)返回的是null
4.6 運行項目
選擇任意一個日期,可以發(fā)現(xiàn)彈出的toast是!!!! year年month月day日,即原生的setOnDateChangeListener覆蓋了Compose中的setOnDateChangeListener監(jiān)聽,這樣說明我們也在原生代碼處,取到了Compose內(nèi)部的原生View了。
5. 本文源碼下載
本文源碼下載地址 : 傳送門
到此這篇關(guān)于Android View與Compose互相調(diào)用實例探究的文章就介紹到這了,更多相關(guān)Android View與Compose 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android之Intent附加數(shù)據(jù)的兩種實現(xiàn)方法
這篇文章主要介紹了Android之Intent附加數(shù)據(jù)的兩種實現(xiàn)方法,以實例形式較為詳細(xì)的分析了添加數(shù)據(jù)到Intent的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-09-09
Android自定義TextBanner實現(xiàn)自動滾動
這篇文章主要為大家詳細(xì)介紹了Android自定義TextBanner實現(xiàn)自動滾動,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-07-07
Android編程操作聯(lián)系人的方法(查詢,獲取,添加等)
這篇文章主要介紹了Android編程操作聯(lián)系人的方法,包括針對聯(lián)系人的查詢,獲取,添加等操作,具有一定參考借鑒價值,需要的朋友可以參考下2016-01-01
Android開發(fā)中button按鈕的使用及動態(tài)添加組件方法示例
這篇文章主要介紹了Android開發(fā)中button按鈕的使用及動態(tài)添加組件方法,涉及Android針對button按鈕的事件響應(yīng)及TextView動態(tài)添加相關(guān)操作技巧,需要的朋友可以參考下2017-11-11

