Compose和Android View相互使用

devtools/2024/9/23 9:12:16/

文章目录

  • Compose和Android View相互使用
    • Compose中使用View
      • 概述
      • 简单控件
      • 复杂控件
      • 嵌入XML布局
    • 在View中使用Compose
    • 组合使用

ComposeAndroid_View_2">Compose和Android View相互使用

ComposeView_4">在Compose中使用View

概述

Compose是一个全新的UI框架,虽然重写了我们熟悉的很多控件,但不可能面面俱到,比如Android View中的一些复杂控件Compose并没有重写。

简单控件

属性:

@Composable
@UiComposable
fun <T : View> AndroidView(factory: (Context) -> T, // Android Viewmodifier: Modifier = Modifier, // 修饰符update: (T) -> Unit = NoOpUpdate // 加载布局后回调
)

使用:

在这里插入图片描述

AndroidView(factory = { CalendarView(it) },modifier = Modifier.fillMaxSize(),update = {it.setOnDateChangeListener { view, year, month, dayOfMonth ->Toast.makeText(view.context, "${year}${month}${dayOfMonth}日", Toast.LENGTH_SHORT).show()}}
)

复杂控件

使用WebView、MapView等控件时需要在对应生命周期中调用对应方法,否则会引起内存泄漏。

Compose 中如果需要根据生命周期来进行不同操作,就需要使用 LocalLifecycleOwner。通过 LocalLifecycleOwner 可以获取当前的lifecycle,然后在控件创建的时候加上监听,之后在关闭的时候关掉监听。

@Composable
fun rememberWebViewWithLifecycle(): WebView {val context = LocalContext.currentval webView = remember {WebView(context)}val lifecycleObserver = rememberWebViewLifecycleObserve(webView)val lifecycle = LocalLifecycleOwner.current.lifecycleDisposableEffect(lifecycle) {lifecycle.addObserver(lifecycleObserver)onDispose {lifecycle.removeObserver(lifecycleObserver)}}return webView
}@Composable
fun rememberWebViewLifecycleObserve(webView: WebView): LifecycleEventObserver {return remember(webView) {LifecycleEventObserver { source, event ->when (event) {Lifecycle.Event.ON_RESUME -> webView.onResume()Lifecycle.Event.ON_PAUSE -> webView.onPause()Lifecycle.Event.ON_DESTROY -> webView.destroy()else -> android.util.Log.e("TAG", "hello world")}}}
}@SuppressLint("SetJavaScriptEnabled")
@Composable
fun MyAndroidView() {val webView = rememberWebViewWithLifecycle()AndroidView(factory = { webView },modifier = Modifier.fillMaxSize(),update = { webView ->webView.settings.apply {javaScriptEnabled = true}webView.loadUrl("https://www.baidu.com")})
}

嵌入XML布局

如果大家在重构项目时遇到复杂的XML布局不易使用Compose来构建,也可以直接在Compose中使用XML布局,不过Compose目前只支持以ViewBinding的方式构建的XML布局。

开启ViewBinding:

viewBinding {enabled = true
}

添加依赖库:

implementation "androidx.compose.ui:ui-viewbinding:1.3.0-beta02"

属性:

fun <T : ViewBinding> AndroidViewBinding(// 创建ViewBindingfactory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T,// 修饰符modifier: Modifier = Modifier,// 加载完后回调update: T.() -> Unit = {}
) 

使用:

在这里插入图片描述

login_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/et_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="30dp"android:hint="name" /><EditTextandroid:id="@+id/et_password"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginHorizontal="30dp"android:hint="password" /><Buttonandroid:id="@+id/btn_login"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_marginTop="30dp"android:text="登录" />
</LinearLayout>

MainActivity

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {Scaffold() { paddingValues ->Box(modifier = Modifier.padding(paddingValues)) {MyAndroidXml()}}}}fun doLogin(name: String, password: String) {if (name.isEmpty() || password.isEmpty()) {Toast.makeText(this, "用户名密码不能为空", Toast.LENGTH_SHORT).show()return}Toast.makeText(this, "用户名:$name 密码:$password", Toast.LENGTH_SHORT).show()}
}@Composable
fun MyAndroidXml() {val context = LocalContext.current as MainActivityAndroidViewBinding(factory = { inflater, parent, attachToParent ->LoginLayoutBinding.inflate(inflater, parent, attachToParent)},modifier = Modifier.fillMaxSize(),update = {btnLogin.setOnClickListener {val name = etName.text.toString().trim()val password = etPassword.text.toString().trim()context.doLogin(name, password)}})
}

Compose_209">在View中使用Compose

概述

在 Android View 中也可以使用 Compose,平时编写 Android 代码的时候一般会使用 Activity 或 Fragment 来展示页面。

Compose_215">在Activity中使用Compose

添加依赖库:

如果是新建的Compose项目,编译器会直接帮我们引入 activity-compose 的依赖;如果是老项目,就需要我们手动添加依赖。

implementation 'androidx.activity:activity-compose:1.3.1'

通过 setContent 方式使用 Compose

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {Text("hello world")}}
}

Compose_238">在Fragment中使用Compose

class MyFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View {val composeView = ComposeView(requireContext()).apply {setContent {Text("hello world")}}return composeView}
}
ComposeView_257">布局使用多个ComposeView

如果一个布局中存在多个ComposeView,那么每个ComposeView必须有唯一ID才能使saveInstanceState发挥作用。

class MyFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View {val linearLayout = LinearLayout(requireContext()).apply {orientation = LinearLayout.VERTICALval oneComposeView = ComposeView(requireContext()).apply {id = R.id.compose_onesetContent {Text("hello")}}addView(oneComposeView)val button = Button(requireContext()).apply {text = "world"}addView(button)val twoComposeView = ComposeView(requireContext()).apply {id = R.id.compose_twosetContent {Text("compose")}}addView(twoComposeView)}return linearLayout}
}

Compose_294">在布局中使用Compose

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/container"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/et_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="30dp" /><androidx.compose.ui.platform.ComposeViewandroid:id="@+id/compose_view"android:layout_width="match_parent"android:layout_height="wrap_content" /></LinearLayout>

代码:

class MainActivity : AppCompatActivity() {private lateinit var activityMainBinding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)activityMainBinding = ActivityMainBinding.inflate(layoutInflater)setContentView(activityMainBinding.root)initViews()}private fun initViews() {activityMainBinding.apply {composeView.setContent {var content by remember { mutableStateOf("") }Column(modifier = Modifier.fillMaxSize()) {Button(onClick = { content = etInput.text.toString().trim() }) {Text("提交")}Text(content)}}}}
}

组合使用

目前大部分应用都是基于 Android View 编写的,而 Android View 只能显示 View,因此需要将 Compose 转为 Android View 中使用的 View。

第一步:创建Compose

@Composable
fun rememberWebViewWithLifecycle(): WebView {val context = LocalContext.currentval webView = remember {WebView(context)}val lifecycleObserver = rememberWebViewLifecycleObserve(webView)val lifecycle = LocalLifecycleOwner.current.lifecycleDisposableEffect(lifecycle) {lifecycle.addObserver(lifecycleObserver)onDispose {lifecycle.removeObserver(lifecycleObserver)}}return webView
}@Composable
fun rememberWebViewLifecycleObserve(webView: WebView): LifecycleEventObserver {return remember(webView) {LifecycleEventObserver { source, event ->when (event) {Lifecycle.Event.ON_RESUME -> webView.onResume()Lifecycle.Event.ON_PAUSE -> webView.onPause()Lifecycle.Event.ON_DESTROY -> webView.destroy()else -> android.util.Log.e("TAG", "hello world")}}}
}@SuppressLint("SetJavaScriptEnabled")
@Composable
fun WebViewPage() {val webView = rememberWebViewWithLifecycle()AndroidView(factory = { webView },modifier = Modifier.fillMaxSize(),update = { webView ->webView.settings.apply {javaScriptEnabled = true}webView.loadUrl("https://www.baidu.com")})
}

第二步:将Compose转为Android View

class MyAndroidView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {@Composableoverride fun Content() {WebViewPage()}
}

第三步:使用Android View

<com.example.app222.MyAndroidViewandroid:layout_width="match_parent"android:layout_height="match_parent" />

http://www.ppmy.cn/devtools/19722.html

相关文章

前端性能优化篇之懒加载的概念、特点、实现原理、懒加载与预加载的区别

目录 懒加载的概念懒加载的特点懒加载的实现原理懒加载与预加载的区别 懒加载的概念 懒加载&#xff0c;也叫做延迟加载或按需加载&#xff0c;是一种用来优化网页性能的方法。在包含许多图片的长网页或应用中&#xff0c;如果一开始就加载所有图片&#xff0c;会导致页面加载…

Facebook的魅力魔法:探访数字社交的奇妙世界

1. 社交媒体的演变与Facebook的角色 在数字化时代&#xff0c;社交媒体已经成为我们日常生活中不可或缺的一部分。而在众多的社交媒体平台中&#xff0c;Facebook 以其深厚的历史和广泛的影响力&#xff0c;成为了全球数亿用户沟通、分享和互动的主要场所。从其初创之时起&…

富唯智能:打造未来机器人教育新标杆

随着科技的飞速发展&#xff0c;机器人教育正逐渐成为培养未来人才的重要领域。富唯智能&#xff0c;作为业内领先的机器人技术提供商&#xff0c;近日推出了一款全新的机器人教育实践平台系统&#xff0c;旨在为学生提供更加丰富、更具挑战性的学习体验。 该平台系统以AUBO-i5…

Flutter 音视频播放器与弹幕系统开发实践

​ 微信公众号&#xff1a;小武码码码 在 Flutter 开发项目的同时&#xff0c;我一直在关注如何利用 Flutter 强大的跨平台特性和丰富的插件生态&#xff0c;来实现媲美原生开发的音视频播放器和弹幕系统。在最近的一个项目中&#xff0c;我将这些想法付诸实践&#xff0c;开…

map_or

map_or 是 Rust 中 Option<T> 和 Result<T, E> 类型提供的一个方法&#xff0c;用于对包含的值执行某种映射操作&#xff0c;并在值不存在&#xff08;对于 Option<T> 是 None&#xff0c;对于 Result<T, E> 是 Err(_)&#xff09;时提供一个默认值。其…

【Flutter 面试题】 为什么Flutter中的Widget使用const注解?

【Flutter 面试题】 为什么Flutter中的Widget使用const注解? 文章目录 写在前面口述回答补充说明示例代码说明写在前面 🙋 关于我 ,小雨青年 👉 CSDN博客专家,GitChat专栏作者,阿里云社区专家博主,51CTO专家博主。2023博客之星TOP153。 👏🏻 正在学 Flutter 的同…

Windows自动化重启python脚本

博主遇到一个需求&#xff1a;每天都需要定时重启一个python脚本&#xff0c;这个脚本是跑在windows电脑上的&#xff0c;下面是博主的解决方法&#xff1a; 获取已经启动的应用的IDkill这个应用ID再次根据指定的Python环境运行这个脚本 文章目录 解决步骤1. 获取已经启动的应…

jQuery

jQuery是一个快速、简洁的JavaScript库&#xff0c;由John Resig创建。它使得HTML文档遍历、事件处理、动画和Ajax等操作更加简单&#xff0c;同时提供了丰富的插件&#xff0c;可以快速开发交互性强的网页应用。jQuery已经成为最受欢迎的JavaScript库之一&#xff0c;被广泛应…