从 XML 到 Compose 快速迁移指南

前言

日常工作,业务需求中的界面布局是至关重要的一部分,长期以来,我们一直都是使用XML布局文件来定义自己的用户界面,但随着Jetpack Compose已经崭露头角,无疑成为了一种强大的UI工具,允许我们以更声明性和可组合的方式构建用户界面。

到目前为止,Compose正在逐步取代XML,越来越多的技术团队已经开始普及使用。下面笔者将以初学者的角度出发,简单阐述如何将现有的XML布局文件转换为Jetpack Compose,实现无缝衔接,以便逐步迁移应用程序的用户界面。

为什么要使用Jetpack Compose

Jetpack ComposeGoogle官方提供的声明式UI工具包,它引入了一种全新的方式来构建用户界面,基于Kotlin编写,而Kotlin作为目前Android的第一生产力语言,这无疑是相得益彰的。由于笔者长期使用XML编写布局,虽然近期才开始重新学习Compose并开始实践用于工作和学习当中, 但Compose必定是未来大势所趋的;虽说目前国内还未完全普及,应用市场上还是有很多应用还在使用着传统的XML布局;

不得不说,现在更新后的AS(笔者这里是Android Studio Giraffe | 2022.3.1 Patch 2)创建的新项目都是默认使用Compose进行界面编写,完全摒弃了传统XML界面布局,这已经说明Compose基本上成为了Google官方主推的UI工具包了;

1.png

这时候有人就会说了,既然会成为主流,那它一定有巨大的优势吧;没错,与传统的XML布局文件相比,Jetpack Compose提供了以下优势:

  • 声明性UI编程Compose允许以声明性的方式描述UI,而不再需要手动操作XML布局文件。这使得UI代码更易于阅读、维护和调试。
  • 可组合性Compose的核心概念是可组合性。可以将小部件组合在一起,构建复杂的UI元素,而不必担心繁琐的布局层次。
  • 实时预览Compose提供了实时预览功能,可以在编写代码的同时查看UI的外观和行为,提高了开发效率。
  • 更少的样板代码:与XML布局相比,Compose代码通常更少,因为它减少了样板代码的需要。
  • 动态性Compose允许在运行时动态更改UI,这在某些情况下非常有用。

从XML 迁移到Compose

由于最近一个小项目需要将之前的传统布局代码全部迁移为Compose,虽然整体项目布局文件不算太多,但这过程就我而言,非常繁琐,需要自己对传统布局以及Compose相应的替代方案有一定的熟练度,难度不大,不过相对于初学者来说真的挺折磨,毕竟是在旧项目上操刀,尤其是在替换后需要处理布局的差异,要尽可能保持一致,不影响现有效果;话不多说,下面是笔者简单替换的流程图(仅供参考)

untitled.png

步骤1:创建Compose界面

首先,我们需要在Compose中创建一个等效的界面。对于每个XML布局,我们将创建一个Compose函数,以相同的UI元素作为参数。举个例子,如果我们有一个XML布局文件包含一个TextView和一个Button,我们可以创建如下的Compose函数:

@Composable
fun MyComposeScreen(text: String, buttonText: String) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = text)
        Button(onClick = { /* 点击处理逻辑 */ }) {
            Text(text = buttonText)
        }
    }
}
步骤2:将数据绑定到Compose界面

接下来,我们需要将数据从XML布局中绑定到Compose界面。这通常涉及创建ViewModel或使用其他数据绑定技术,以确保数据在Compose界面中正确显示。

步骤3:逐步替换XML

一旦Compose界面准备好,并且数据绑定完成,我们可以开始逐步替换XML布局。在每个步骤中,我们将删除XML布局的引用,并在适当的位置调用我们的Compose函数。

这里作为初学者,可以给大家推荐一个网站

  • Which Compose API to use

    在替换的时候,有人肯定想问,如果使用RecyclerView的话,我们在Compose中应该用什么呢,ProgressBar我们又该用什么替换呢,等等相关疑问。这个网站的宗旨就是输入传统布局组件它会告诉我们应该用什么,非常实用,直接对症下药,可进一步提高开发效率

whats.png

步骤4:处理布局差异

在某些情况下,XML布局和Compose之间可能存在布局差异。例如,Compose使用Modifier来管理布局属性,而XML布局使用约束和布局参数。在这种情况下,您需要确保在Compose中正确处理这些差异,以确保UI的外观和行为保持一致。

步骤5:测试和迭代

最后,不要忘记对迁移后的界面进行彻底测试,并根据需要进行迭代和修复。

关于Compose替换XML

在逐步替换XML的过程中,如果在不中断现有功能的情况下实现无缝衔接的话,需要对XML布局有一定的理解和认识,所以为了在转化过程中更加顺利流畅,下面笔者XML中一些常用的布局和组件的替换方案,一起看看在Compose中是怎么实现的吧

这里笔者简单统计了下开发过程中常用的一些布局/组件替换方案,由于代码过多,将以图文的形式展示,方便大家参考

替代xml中的常用布局

我们常用的六大布局,相信大家已经非常熟悉了,就不过多解释啦,下面是它们在Compose的替代方案

xml布局-_Compose布局.png

1. LinearLayout

XML中,LinearLayout用于线性排列其子视图,可以垂直或水平排列。在Compose中,我们可以使用ColumnRow来实现类似的布局,简单替换代码如下图所示

LinearLayout.png

水平布局也是如此,换成Row即可

2. RelativeLayout

RelativeLayout允许在XML中相对于其他视图定位子视图。在Compose中,我们可以使用BoxModifier相互配合来实现类似的布局,具体得根据实际项目出发,简单替换代码如下图所示

RelativeLayout.png

3. FrameLayout

FrameLayout作为帧布局,主要就是为了堆叠子视图,在Compose中,可以使用Box来实现类似的效果,但要注意Box中的子视图是按照层叠顺序绘制的,简单替换代码如下所示

FrameLayout.png

4. GridLayout

GridLayout用于构建灵活的网格布局,在Compose中,由于之前的Grid组件已经被废弃了,目前需要根据项目本身的需求去自定义布局实现,简单替换代码如下图所示

GridLayout.png

我们也可以使用LazyVerticalGrid / LacyHorizontalGrid去实现,它们是Compose中专门用于网格布局的组件,这里简单举一个示例

 val itemsList = List(12) { index -> "Item $index" }
    LazyVerticalGrid(columns = GridCells.Fixed(2),
        modifier = Modifier.fillMaxSize(), content = {
            items(itemsList) { item ->
                // 创建并放置子视图
                Text(
                    text = item,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp)
                )
            }
        })

这里使用LazyVerticalGrid来创建一个垂直网格布局,通过GridCells.Fixed(2)指定了每行的列数为2。然后,我们使用items函数来迭代并放置子视图

5. TableLayout

TableLayout在XML中用于创建表格布局,通常用于显示数据表格或多行多列的内容。在Compose中,可以使用LazyColumn或LazyRow来创建类似的表格布局,简单替换代码如下所示:

图片

在Compose中,我们可以使用LazyColumn来创建表格布局,通过嵌套Row和Text组件来实现表格行和单元格。通过调整items的数量和添加更多的表格行,可以创建一个多行多列的表格布局

6. ConstraintLayout

ConstraintLayout无疑是我们日常使用最频繁的布局了,它非常强大,可以用来实现一些复杂的界面,在Compose中没有直接对应的布局,需要我们额外引入相对应依赖库来实现约束布局的效果

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"

简单替换代码如下图所示

图片

Compose中,使用ConstraintLayout创建了两个子视图,并使用constrainAs修饰符定义了它们之间的约束关系

替代xml中的常用组件

XML中我们平常会用到很多很多组件,针对这些组件的替换,Compose也提供了相应的解决方案,如下图所示

xml组件-_Compose组件.png

1. Button,ImageView,TextView

这些组件平常写传统布局的时候一定离不开它们,而它们在Compose中的替代方案也非常简单,分别是:

  • Button组件允许我们创建可交互的按钮,可以自定义文本、样式和点击事件
  • Text组件用于显示文本内容,支持自定义样式和文本格式。
  • Image组件用于显示图像,支持从资源、网络或本地文件加载图像。

简单替换代码如下图所示

Button,ImageView,Text.png

2. CardView

CardViewXML中用于创建卡片式布局,常用于显示列表项或单独的信息块。在Compose中,可以使用Card组件来实现类似的效果,简单替换代码如下所示

Card.png

可以设置卡片的elevation(阴影)shape(形状) 等属性

3. RecyclerView

RecyclerViewXML中用于显示大量数据列表,是Android应用中常见的UI组件之一;在Compose中,您可以使用LazyColumnLazyRow来创建类似的可滚动列表,简单替换代码如下图所示

recyclerview.png

使用LazyColumn可以轻松创建可滚动的垂直列表,而LazyRow用于创建水平列表。在Compose中,使用items函数来绑定数据到列表项,并在其中创建列表项的内容

4. ViewPager,ViewPager2

ViewPager、ViewPager2XML中用于创建可滑动的页面容器,通常用于实现轮播图或分页浏览;在Compose中没有直接内置的ViewPager相关组件,这里笔者引入了Accompanist里的库,使用HorizontalPager、VerticalPager来完成类似的效果

implementation "com.google.accompanist:accompanist-pager-indicators:0.22.0-rc"
implementation "com.google.accompanist:accompanist-pager:0.22.0-rc"

简单替换代码如下图所示

图片

5. AppBarLayout

AppBarLayoutXML中常用于创建标题栏,通常包含标题、操作按钮等。在Compose中,可以使用TopAppBar来实现标题栏效果,简单替换代码如下图所示

AppBarLayout.png

TopAppBar可以设置标题、背景颜色、文本颜色以及操作按钮等属性。

6. TabLayout

TabLayoutXML中用于创建选项卡式布局,通常用于导航不同的标签页。在Compose中,可以使用TabRow来实现类似的选项卡效果,简单替换代码如下图所示

TabLayout.png

TabRowTab可以实现选项卡式布局,并在用户与选项卡进行交互时更新界面

7. BottomNavigationView

BottomNavigationViewXML中常用于底部导航栏,用于切换不同的页面或模块。在Compose中,可以使用BottomNavigationBottomNavigationItem来实现底部导航栏效果,简单替换代码如下图所示

BottomNavigationView.png

使用BottomNavigationBottomNavigationItem可以轻松创建底部导航栏,并实现页面切换效果

8. SearchView

SearchViewXML中用于创建搜索框,通常用于搜索功能。在Compose中,可以使用TextFieldIcons.Default.Search等组件来创建搜索框,简单替换代码如下图所示

SearchView.png

使用TextFieldIcon和状态管理,可以创建具有搜索和清除功能的搜索框,是不是更加方便了呢

9. ScrollView

ScrollViewXML中用于滚动一个或多个子视图,以便在屏幕上显示超出可见区域的内容。在Compose中,可以使用LazyColumn来实现类似的滚动效果,简单替换代码如下所示

ScrollView.png

LazyColumn基本适用于常见的滚动列表,它会根据需要延迟加载内容

10. ProgressBar

ProgressBarXML中用于显示加载进度,通常用于等待长时间操作或网络请求。在Compose中,可以使用LinearProgressIndicator来创建加载进度条,简单替换代码如下图所示

ProgressBar.png

使用LinearProgressIndicator可以轻松创建加载进度条,并使用状态管理来控制进度

11. WebView

WebViewXML中用于显示Web页面或嵌入网页内容。在Compose中,可以使用AndroidView来嵌入WebView,简单替换代码如下所示

WebView.png

创建一个嵌入的WebView,并加载指定的URL

12. Switch

SwitchXML中用于创建开关按钮,通常用于切换某个设置或状态。在Compose中,可以使用Switch组件来实现类似的开关按钮,简单替换代码如下所示

Switch.png

13. DrawerLayout

DrawerLayoutXML中用于创建抽屉式布局,通常用于侧边栏导航。在Compose中,可以使用ModalDrawer来实现侧边栏效果,简单替换代码如下图所示

DrawerLayout.png

使用ModalDrawer可以实现抽屉式布局,可以控制抽屉的状态并自定义抽屉和内容

以上就是一些常见的组件简单替换方案,具体的要根据项目需求,选择适合的Compose布局元素和修饰符,以满足我们的UI设计需求

最后想说的话

目前来说,国内Compose并没有完全替代XML传统布局,但将XML布局转换为Compose无疑是一项重要的工作,它可以带来许多好处,包括更好的可维护性、更高的开发效率和更好的用户体验。随着项目版本迭代,通过逐步迁移,我们可以逐渐引入Compose,并在不中断现有功能的情况下实现无缝衔接。

作者:RainyJiang
链接:https://juejin.cn/post/7288151382533390395
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。