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" />
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |