Compose和Android View相互使用

news2025/1/17 16:02:03

文章目录

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

Compose和Android View相互使用

在Compose中使用View

概述

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

简单控件

属性:

@Composable
@UiComposable
fun <T : View> AndroidView(
    factory: (Context) -> T, // Android View
    modifier: 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.current
    val webView = remember {
        WebView(context)
    }
    val lifecycleObserver = rememberWebViewLifecycleObserve(webView)
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    DisposableEffect(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(
    // 创建ViewBinding
    factory: (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">

    <EditText
        android:id="@+id/et_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:hint="name" />

    <EditText
        android:id="@+id/et_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="30dp"
        android:hint="password" />

    <Button
        android: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 MainActivity
    AndroidViewBinding(
        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)
            }
        }
    )
}

在View中使用Compose

概述

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

在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")
        }
    }
}

在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

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

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

在布局中使用Compose

  • 在XML布局中使用ComposeView。
  • 通过ComposeView的setContent设置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">

    <EditText
        android:id="@+id/et_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="30dp" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

代码:

class MainActivity : AppCompatActivity() {
    private lateinit var activityMainBinding: ActivityMainBinding

    override 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.current
    val webView = remember {
        WebView(context)
    }
    val lifecycleObserver = rememberWebViewLifecycleObserve(webView)
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    DisposableEffect(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) {

    @Composable
    override fun Content() {
        WebViewPage()
    }
}

第三步:使用Android View

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1628228.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web自动化系列-selenium的下拉框定位(十三)

在功能操作过程中 &#xff0c;遇到下拉列表是很正常的事 &#xff0c;比如像一些查询条件就都是使用的是下来列表 。所以 &#xff0c;selenium也需要支持对下拉框的操作 。 1.下拉列表 在selenium中&#xff0c;也提供了一个下拉列表操作的类 &#xff1a;Select . 以下为该…

C语言学习/复习35

一、语言文件操作 1.章节重点 注意事项1&#xff1a;可变参数 、

使用NGINX做局域网内 浏览器直接访问链接 拓展外网链接访问本地

达成目的功能&#xff1a; 在本地服务的一个文件路径下&#xff0c;局域网内用ip和路径名访问到对应的地址&#xff1b;如 10.5.9.0/v1 即可访问到 某个固定本地地址目录 V1下&#xff0c;名为index.html的文件。前言 NGINX 是一个非常流行的开源 Web 服务器和反向代理服务器…

【踩坑日记】SpringBoot集成Kafka,消息没有按照顺序消息问题【已解决】

背景 作为一个合格的码农&#xff0c;当然要学会CV大法了&#xff0c;可是CV也是有风险的&#xff0c;别以为前任写的已经上线那么久了没有问题… 我们需要将埋点信息上报到一个三方平台&#xff08;S2S&#xff09;接口&#xff0c;三方平台对时间有要求&#xff0c;同一个用…

【工具】-根源上解决VScode打印输出乱码的问题

目录 1 第一步&#xff1a; 改编译命令&#xff0c;保持一致2 第二步&#xff1a; 更改VScode的编码格式-保持一致 1 第一步&#xff1a; 改编译命令&#xff0c;保持一致 看一下你的控制台的编译的命名后缀&#xff0c;有两个关键的参数&#xff0c;如下图&#xff1a; “-f…

预测性营销在业务经营中的实践:开源AI名片商城系统小程序的应用与实例

在数字化浪潮中&#xff0c;预测性营销已经成为企业获取市场竞争优势、推动业务增长的关键手段。而开源AI名片商城系统小程序&#xff0c;作为一种融合了先进技术和智能分析能力的工具&#xff0c;为企业在业务经营中实现预测性营销提供了有力支持。 首先&#xff0c;让我们通过…

神经网络中多层卷积的作用

在神经网络中采用多层卷积的目的是为了逐步提取和组合图像的抽象特征&#xff0c;从而更有效地学习数据的表示并执行复杂的任务。不同层的卷积具有不同的作用&#xff0c;从较低层次的特征&#xff08;例如边缘、纹理&#xff09;到较高层次的抽象特征&#xff08;例如物体部件…

怎么做视频二维码更方便?在线一键生成视频活码二维码

现在经常会发现很多的二维码可以用来展示视频内容&#xff0c;通过这种方式来实现视频的快速分享与传播。二维码是一种成本低传播快的内容传播方式&#xff0c;很多的内容都可以通过生成二维码的方式来分享给其他人&#xff0c;可以同时扫描相同的二维码来获取内容&#xff0c;…

企业进行数字化转型需要具备哪些基础条件?

蚓链实践——企业进行数字化转型通常需要具备以下基础条件&#xff1a; 1. 管理层的支持&#xff1a;高层管理者对数字化转型的理解和支持至关重要。 2. 明确的战略目标&#xff1a;确定数字化转型的目标&#xff0c;以指导决策和资源分配。 3. 数据基础&#xff1a;拥有良好…

【MySQL】MySQL中MVCC多版本并发控制的概念

MySQL中MVCC多版本并发控制的概念 锁相关的知识我们已经学习完了&#xff0c;在其中我们提到过一个概念&#xff0c;那就是 MVCC 。这又是个什么东西呢&#xff1f;今天我们就来好好看看 MVCC 到底是干嘛的。 MVCC 多版本并发控制&#xff0c;它主要是控制 读 操作&#xff0c;…

【Java系列】SpringCloudAlibaba 实现在不修改配置文件情况下适配不同环境部署

本文将向大家介绍在SpringCloudAlibaba微服务架构中&#xff0c;如何实现多个微服务在不修改各自配置文件的情况下适配不同环境进行部署。 作者&#xff1a;后端小肥肠 1. 前言 在现代软件开发过程中&#xff0c;随着敏捷开发和持续集成的普及&#xff0c;开发团队越来越需要在…

Linux c++ onvif客户端开发(8):GetServices

本文是Linux c onvif客户端开发系列文章之一&#xff1a; Linux c onvif客户端开发(1): 根据wsdl生成cpp源文件Linux c onvif客户端开发(2): 获取摄像头H264/H265 RTSP地址Linux c onvif客户端开发(3): 扫描设备Linux c onvif客户端开发(4): 扫描某个设备是否支持onvifLinux c…

2021 年全国职业院校技能大赛高职组“信息安全管理与评估”赛项 A 卷 第二阶段任务书

第二阶段任务书 一、赛项第二阶段时间180 分钟。 信息安全管理与评估 网络系统管理 网络搭建与应用 云计算 软件测试 移动应用开发 任务书&#xff0c;赛题&#xff0c;解析等资料&#xff0c;知识点培训服务 添加博主wx&#xff1a;liuliu5488233 三、注意事项 赛题第二阶段请…

BUUCTF--web(1)

1、[极客大挑战 2019]Http1 1.http报文请求&#xff1a; 1、请求行&#xff1a; 第一部分是请求方法&#xff0c;常见包括GET、POST、OPTIONS&#xff08;我目前还没有见过我是菜鸡&#xff09; 第二部分是url 第三部分是HTTP协议(http(Hypertext transfer protocol)超文本传…

CMakeLists.txt中如何添加编译选项?

1. 引子 编译器有多种可供选择&#xff0c;如g、c、clang等&#xff0c;如下以c作为示例。 2. 使用CMAKE_CXX_FLAGS添加编译选项 在Makefile中可能用类似如下的指令来添加编译选项&#xff1a; /usr/bin/c -Wall -Wextra -Wno-sign-compare -Wno-unused-variable -Wno-unuse…

网络基础(day3)

【 理论重点】 网络是什么&#xff1f; &#xff08;网络是载体&#xff0c;目的是传输互联网中的数据&#xff0c;数据是终端产生<手机、电脑、服务器等>。&#xff09; 如何组件网络&#xff08;良性网络架构&#xff09;&#xff1f;有网络架构思维&#xff0c;得按层…

C 函数递归

目录 什么是递归 递归的限制条件 递归的例子 1、用递归求n的阶乘 递归扩展学习 1、青蛙跳台阶 思路 代码实现 2、汉诺塔问题​ 思路 代码实现 总结 什么是递归 递归&#xff1a;“递推” “回归” 在C语言中&#xff0c;函数递归就是&#xff1a;函数自己调用自…

【docker】安装openjdk

查看可用的 openjdk版本 docker hub 查看地址&#xff1a;https://hub.docker.com/_/openjdk 此图片已被正式弃用&#xff0c;建议所有用户尽快找到并使用合适的替代品。其他官方形象替代品的一些例子&#xff08;按字母顺序列出&#xff0c;没有有意或暗示的偏好&#xff09;…

Python语言在地球科学交叉领域中的应用

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;Python能够运行在Linux、Windows、Macintosh、AIX操作系统上及不同平台&#xff08;x86和arm&#xff09;&#xff0c;Python简洁的语法和对动态输入的支持&#xff0c;再加上解释性语言的本质&…

羊大师分析,羊奶相伴五一畅享自然时光

羊大师分析&#xff0c;羊奶相伴五一畅享自然时光 羊奶相伴&#xff0c;五一畅享自然时光&#xff0c;这是一句富有诗意和生活气息的语句。羊奶&#xff0c;作为一种营养丰富、易于消化的饮品&#xff0c;不仅为人们提供了优质的蛋白质、矿物质和维生素&#xff0c;还因其独特…