ToB企服应用市场:ToB评测及商务社交产业平台

标题: Compose和Android View相互使用 [打印本页]

作者: 伤心客    时间: 2024-7-30 23:17
标题: Compose和Android View相互使用
Compose和Android View相互使用

在Compose中使用View

概述

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

属性:
  1. @Composable
  2. @UiComposable
  3. fun <T : View> AndroidView(
  4.     factory: (Context) -> T, // Android View
  5.     modifier: Modifier = Modifier, // 修饰符
  6.     update: (T) -> Unit = NoOpUpdate // 加载布局后回调
  7. )
复制代码
使用:

  1. AndroidView(
  2.     factory = { CalendarView(it) },
  3.     modifier = Modifier.fillMaxSize(),
  4.     update = {
  5.         it.setOnDateChangeListener { view, year, month, dayOfMonth ->
  6.                                     Toast.makeText(view.context, "${year}年${month}月${dayOfMonth}日", Toast.LENGTH_SHORT).show()
  7.                                    }
  8.     }
  9. )
复制代码
复杂控件

使用WebView、MapView等控件时必要在对应生命周期中调用对应方法,否则会引起内存泄漏。
在 Compose 中如果必要根据生命周期来进行不同操纵,就必要使用 LocalLifecycleOwner。通过 LocalLifecycleOwner 可以获取当前的lifecycle,然后在控件创建的时候加上监听,之后在关闭的时候关掉监听。
  1. @Composable
  2. fun rememberWebViewWithLifecycle(): WebView {
  3.     val context = LocalContext.current
  4.     val webView = remember {
  5.         WebView(context)
  6.     }
  7.     val lifecycleObserver = rememberWebViewLifecycleObserve(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. }
  17. @Composable
  18. fun rememberWebViewLifecycleObserve(webView: WebView): LifecycleEventObserver {
  19.     return remember(webView) {
  20.         LifecycleEventObserver { source, event ->
  21.             when (event) {
  22.                 Lifecycle.Event.ON_RESUME -> webView.onResume()
  23.                 Lifecycle.Event.ON_PAUSE -> webView.onPause()
  24.                 Lifecycle.Event.ON_DESTROY -> webView.destroy()
  25.                 else -> android.util.Log.e("TAG", "hello world")
  26.             }
  27.         }
  28.     }
  29. }
  30. @SuppressLint("SetJavaScriptEnabled")
  31. @Composable
  32. fun MyAndroidView() {
  33.     val webView = rememberWebViewWithLifecycle()
  34.     AndroidView(
  35.         factory = { webView },
  36.         modifier = Modifier.fillMaxSize(),
  37.         update = { webView ->
  38.             webView.settings.apply {
  39.                 javaScriptEnabled = true
  40.             }
  41.             webView.loadUrl("https://www.baidu.com")
  42.         }
  43.     )
  44. }
复制代码
嵌入XML布局

如果各人在重构项目时碰到复杂的XML布局不易使用Compose来构建,也可以直接在Compose中使用XML布局,不外Compose现在只支持以ViewBinding的方式构建的XML布局。
开启ViewBinding:
  1. viewBinding {
  2.     enabled = true
  3. }
复制代码
添加依靠库:
  1. implementation "androidx.compose.ui:ui-viewbinding:1.3.0-beta02"
复制代码
属性:
  1. fun <T : ViewBinding> AndroidViewBinding(
  2.     // 创建ViewBinding
  3.     factory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T,
  4.     // 修饰符
  5.     modifier: Modifier = Modifier,
  6.     // 加载完后回调
  7.     update: T.() -> Unit = {}
  8. )
复制代码
使用:

login_layout.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     android:orientation="vertical">
  6.     <EditText
  7.         android:id="@+id/et_name"
  8.         android:layout_width="match_parent"
  9.         android:layout_height="wrap_content"
  10.         android:layout_margin="30dp"
  11.         android:hint="name" />
  12.     <EditText
  13.         android:id="@+id/et_password"
  14.         android:layout_width="match_parent"
  15.         android:layout_height="wrap_content"
  16.         android:layout_marginHorizontal="30dp"
  17.         android:hint="password" />
  18.     <Button
  19.         android:id="@+id/btn_login"
  20.         android:layout_width="wrap_content"
  21.         android:layout_height="wrap_content"
  22.         android:layout_gravity="center"
  23.         android:layout_marginTop="30dp"
  24.         android:text="登录" />
  25. </LinearLayout>
复制代码
MainActivity
  1. class MainActivity : ComponentActivity() {
  2.     override fun onCreate(savedInstanceState: Bundle?) {
  3.         super.onCreate(savedInstanceState)
  4.         setContent {
  5.             Scaffold(
  6.             ) { paddingValues ->
  7.                 Box(modifier = Modifier.padding(paddingValues)) {
  8.                     MyAndroidXml()
  9.                 }
  10.             }
  11.         }
  12.     }
  13.     fun doLogin(name: String, password: String) {
  14.         if (name.isEmpty() || password.isEmpty()) {
  15.             Toast.makeText(this, "用户名密码不能为空", Toast.LENGTH_SHORT).show()
  16.             return
  17.         }
  18.         Toast.makeText(this, "用户名:$name 密码:$password", Toast.LENGTH_SHORT).show()
  19.     }
  20. }
  21. @Composable
  22. fun MyAndroidXml() {
  23.     val context = LocalContext.current as MainActivity
  24.     AndroidViewBinding(
  25.         factory = { inflater, parent, attachToParent ->
  26.             LoginLayoutBinding.inflate(inflater, parent, attachToParent)
  27.         },
  28.         modifier = Modifier.fillMaxSize(),
  29.         update = {
  30.             btnLogin.setOnClickListener {
  31.                 val name = etName.text.toString().trim()
  32.                 val password = etPassword.text.toString().trim()
  33.                 context.doLogin(name, password)
  34.             }
  35.         }
  36.     )
  37. }
复制代码
在View中使用Compose

概述

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

添加依靠库:
如果是新建的Compose项目,编译器会直接帮我们引入 activity-compose 的依靠;如果是老项目,就必要我们手动添加依靠。
  1. implementation 'androidx.activity:activity-compose:1.3.1'
复制代码
通过 setContent 方式使用 Compose。
  1. class MainActivity : ComponentActivity() {
  2.     override fun onCreate(savedInstanceState: Bundle?) {
  3.         super.onCreate(savedInstanceState)
  4.         setContent {
  5.             Text("hello world")
  6.         }
  7.     }
  8. }
复制代码
在Fragment中使用Compose

  1. class MyFragment : Fragment() {
  2.     override fun onCreateView(
  3.         inflater: LayoutInflater,
  4.         container: ViewGroup?,
  5.         savedInstanceState: Bundle?
  6.     ): View {
  7.         val composeView = ComposeView(requireContext()).apply {
  8.             setContent {
  9.                 Text("hello world")
  10.             }
  11.         }
  12.         return composeView
  13.     }
  14. }
复制代码
布局使用多个ComposeView

如果一个布局中存在多个ComposeView,那么每个ComposeView必须有唯一ID才能使saveInstanceState发挥作用。
  1. class MyFragment : Fragment() {
  2.     override fun onCreateView(
  3.         inflater: LayoutInflater,
  4.         container: ViewGroup?,
  5.         savedInstanceState: Bundle?
  6.     ): View {
  7.         val linearLayout = LinearLayout(requireContext()).apply {
  8.             orientation = LinearLayout.VERTICAL
  9.             val oneComposeView = ComposeView(requireContext()).apply {
  10.                 id = R.id.compose_one
  11.                 setContent {
  12.                     Text("hello")
  13.                 }
  14.             }
  15.             addView(oneComposeView)
  16.             val button = Button(requireContext()).apply {
  17.                 text = "world"
  18.             }
  19.             addView(button)
  20.             val twoComposeView = ComposeView(requireContext()).apply {
  21.                 id = R.id.compose_two
  22.                 setContent {
  23.                     Text("compose")
  24.                 }
  25.             }
  26.             addView(twoComposeView)
  27.         }
  28.         return linearLayout
  29.     }
  30. }
复制代码
在布局中使用Compose


布局:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:id="@+id/container"
  4.     android:layout_width="match_parent"
  5.     android:layout_height="match_parent"
  6.     android:orientation="vertical">
  7.     <EditText
  8.         android:id="@+id/et_input"
  9.         android:layout_width="match_parent"
  10.         android:layout_height="wrap_content"
  11.         android:padding="30dp" />
  12.     <androidx.compose.ui.platform.ComposeView
  13.         android:id="@+id/compose_view"
  14.         android:layout_width="match_parent"
  15.         android:layout_height="wrap_content" />
  16. </LinearLayout>
复制代码
代码:
  1. class MainActivity : AppCompatActivity() {
  2.     private lateinit var activityMainBinding: ActivityMainBinding
  3.     override fun onCreate(savedInstanceState: Bundle?) {
  4.         super.onCreate(savedInstanceState)
  5.         activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
  6.         setContentView(activityMainBinding.root)
  7.         initViews()
  8.     }
  9.     private fun initViews() {
  10.         activityMainBinding.apply {
  11.             composeView.setContent {
  12.                 var content by remember { mutableStateOf("") }
  13.                 Column(modifier = Modifier.fillMaxSize()) {
  14.                     Button(onClick = { content = etInput.text.toString().trim() }) {
  15.                         Text("提交")
  16.                     }
  17.                     Text(content)
  18.                 }
  19.             }
  20.         }
  21.     }
  22. }
复制代码
组合使用

现在大部分应用都是基于 Android View 编写的,而 Android View 只能显示 View,因此必要将 Compose 转为 Android View 中使用的 View。
第一步:创建Compose
  1. @Composable
  2. fun rememberWebViewWithLifecycle(): WebView {
  3.     val context = LocalContext.current
  4.     val webView = remember {
  5.         WebView(context)
  6.     }
  7.     val lifecycleObserver = rememberWebViewLifecycleObserve(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. }
  17. @Composable
  18. fun rememberWebViewLifecycleObserve(webView: WebView): LifecycleEventObserver {
  19.     return remember(webView) {
  20.         LifecycleEventObserver { source, event ->
  21.             when (event) {
  22.                 Lifecycle.Event.ON_RESUME -> webView.onResume()
  23.                 Lifecycle.Event.ON_PAUSE -> webView.onPause()
  24.                 Lifecycle.Event.ON_DESTROY -> webView.destroy()
  25.                 else -> android.util.Log.e("TAG", "hello world")
  26.             }
  27.         }
  28.     }
  29. }
  30. @SuppressLint("SetJavaScriptEnabled")
  31. @Composable
  32. fun WebViewPage() {
  33.     val webView = rememberWebViewWithLifecycle()
  34.     AndroidView(
  35.         factory = { webView },
  36.         modifier = Modifier.fillMaxSize(),
  37.         update = { webView ->
  38.             webView.settings.apply {
  39.                 javaScriptEnabled = true
  40.             }
  41.             webView.loadUrl("https://www.baidu.com")
  42.         }
  43.     )
  44. }
复制代码
第二步:将Compose转为Android View
  1. class MyAndroidView @JvmOverloads constructor(
  2.     context: Context,
  3.     attrs: AttributeSet? = null,
  4.     defStyleAttr: Int = 0
  5. ) : AbstractComposeView(context, attrs, defStyleAttr) {
  6.     @Composable
  7.     override fun Content() {
  8.         WebViewPage()
  9.     }
  10. }
复制代码
第三步:使用Android View
  1. <com.example.app222.MyAndroidView
  2.         android:layout_width="match_parent"
  3.         android:layout_height="match_parent" />
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4