从 XML 到 Compose 快速迁移指南
前言
日常工作,业务需求中的界面布局是至关重要的一部分,长期以来,我们一直都是使用XML布局文件来定义自己的用户界面,但随着Jetpack Compose已经崭露头角,无疑成为了一种强大的UI工具,允许我们以更声明性和可组合的方式构建用户界面。
到目前为止,Compose正在逐步取代XML,越来越多的技术团队已经开始普及使用。下面笔者将以初学者的角度出发,简单阐述如何将现有的XML布局文件转换为Jetpack Compose,实现无缝衔接,以便逐步迁移应用程序的用户界面。
为什么要使用Jetpack Compose
Jetpack Compose是Google官方提供的声明式UI工具包,它引入了一种全新的方式来构建用户界面,基于Kotlin编写,而Kotlin作为目前Android的第一生产力语言,这无疑是相得益彰的。由于笔者长期使用XML编写布局,虽然近期才开始重新学习Compose并开始实践用于工作和学习当中, 但Compose必定是未来大势所趋的;虽说目前国内还未完全普及,应用市场上还是有很多应用还在使用着传统的XML布局;
不得不说,现在更新后的AS(笔者这里是Android Studio Giraffe | 2022.3.1 Patch 2)创建的新项目都是默认使用Compose进行界面编写,完全摒弃了传统XML界面布局,这已经说明Compose基本上成为了Google官方主推的UI工具包了;
这时候有人就会说了,既然会成为主流,那它一定有巨大的优势吧;没错,与传统的XML布局文件相比,Jetpack Compose提供了以下优势:
- 声明性UI编程:Compose允许以声明性的方式描述UI,而不再需要手动操作XML布局文件。这使得UI代码更易于阅读、维护和调试。
- 可组合性:Compose的核心概念是可组合性。可以将小部件组合在一起,构建复杂的UI元素,而不必担心繁琐的布局层次。
- 实时预览:Compose提供了实时预览功能,可以在编写代码的同时查看UI的外观和行为,提高了开发效率。
- 更少的样板代码:与XML布局相比,Compose代码通常更少,因为它减少了样板代码的需要。
- 动态性:Compose允许在运行时动态更改UI,这在某些情况下非常有用。
从XML 迁移到Compose
由于最近一个小项目需要将之前的传统布局代码全部迁移为Compose,虽然整体项目布局文件不算太多,但这过程就我而言,非常繁琐,需要自己对传统布局以及Compose相应的替代方案有一定的熟练度,难度不大,不过相对于初学者来说真的挺折磨,毕竟是在旧项目上操刀,尤其是在替换后需要处理布局的差异,要尽可能保持一致,不影响现有效果;话不多说,下面是笔者简单替换的流程图(仅供参考)
步骤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函数。
这里作为初学者,可以给大家推荐一个网站
-
在替换的时候,有人肯定想问,如果使用RecyclerView的话,我们在Compose中应该用什么呢,ProgressBar我们又该用什么替换呢,等等相关疑问。这个网站的宗旨就是输入传统布局组件它会告诉我们应该用什么,非常实用,直接对症下药,可进一步提高开发效率
步骤4:处理布局差异
在某些情况下,XML布局和Compose之间可能存在布局差异。例如,Compose使用Modifier来管理布局属性,而XML布局使用约束和布局参数。在这种情况下,您需要确保在Compose中正确处理这些差异,以确保UI的外观和行为保持一致。
步骤5:测试和迭代
最后,不要忘记对迁移后的界面进行彻底测试,并根据需要进行迭代和修复。
关于Compose替换XML
在逐步替换XML的过程中,如果在不中断现有功能的情况下实现无缝衔接的话,需要对XML布局有一定的理解和认识,所以为了在转化过程中更加顺利流畅,下面笔者XML中一些常用的布局和组件的替换方案,一起看看在Compose中是怎么实现的吧
这里笔者简单统计了下开发过程中常用的一些布局/组件替换方案,由于代码过多,将以图文的形式展示,方便大家参考
替代xml中的常用布局
我们常用的六大布局,相信大家已经非常熟悉了,就不过多解释啦,下面是它们在Compose的替代方案
1. LinearLayout
在XML中,LinearLayout用于线性排列其子视图,可以垂直或水平排列。在Compose中,我们可以使用Column和Row来实现类似的布局,简单替换代码如下图所示
水平布局也是如此,换成Row即可
2. RelativeLayout
RelativeLayout允许在XML中相对于其他视图定位子视图。在Compose中,我们可以使用Box和Modifier相互配合来实现类似的布局,具体得根据实际项目出发,简单替换代码如下图所示
3. FrameLayout
FrameLayout作为帧布局,主要就是为了堆叠子视图,在Compose中,可以使用Box来实现类似的效果,但要注意Box中的子视图是按照层叠顺序绘制的,简单替换代码如下所示
4. GridLayout
GridLayout用于构建灵活的网格布局,在Compose中,由于之前的Grid组件已经被废弃了,目前需要根据项目本身的需求去自定义布局实现,简单替换代码如下图所示
我们也可以使用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也提供了相应的解决方案,如下图所示
1. Button,ImageView,TextView
这些组件平常写传统布局的时候一定离不开它们,而它们在Compose中的替代方案也非常简单,分别是:
- Button组件允许我们创建可交互的按钮,可以自定义文本、样式和点击事件
- Text组件用于显示文本内容,支持自定义样式和文本格式。
- Image组件用于显示图像,支持从资源、网络或本地文件加载图像。
简单替换代码如下图所示
2. CardView
CardView在XML中用于创建卡片式布局,常用于显示列表项或单独的信息块。在Compose中,可以使用Card组件来实现类似的效果,简单替换代码如下所示
可以设置卡片的elevation(阴影) 和shape(形状) 等属性
3. RecyclerView
RecyclerView在XML中用于显示大量数据列表,是Android应用中常见的UI组件之一;在Compose中,您可以使用LazyColumn或LazyRow来创建类似的可滚动列表,简单替换代码如下图所示
使用
LazyColumn
可以轻松创建可滚动的垂直列表,而LazyRow
用于创建水平列表。在Compose中,使用items
函数来绑定数据到列表项,并在其中创建列表项的内容
4. ViewPager,ViewPager2
ViewPager、ViewPager2在XML中用于创建可滑动的页面容器,通常用于实现轮播图或分页浏览;在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
AppBarLayout在XML中常用于创建标题栏,通常包含标题、操作按钮等。在Compose中,可以使用TopAppBar来实现标题栏效果,简单替换代码如下图所示
TopAppBar可以设置标题、背景颜色、文本颜色以及操作按钮等属性。
6. TabLayout
TabLayout在XML中用于创建选项卡式布局,通常用于导航不同的标签页。在Compose中,可以使用TabRow来实现类似的选项卡效果,简单替换代码如下图所示
TabRow
和Tab
可以实现选项卡式布局,并在用户与选项卡进行交互时更新界面
7. BottomNavigationView
BottomNavigationView在XML中常用于底部导航栏,用于切换不同的页面或模块。在Compose中,可以使用BottomNavigation和BottomNavigationItem来实现底部导航栏效果,简单替换代码如下图所示
使用
BottomNavigation
和BottomNavigationItem
可以轻松创建底部导航栏,并实现页面切换效果
8. SearchView
SearchView在XML中用于创建搜索框,通常用于搜索功能。在Compose中,可以使用TextField和Icons.Default.Search等组件来创建搜索框,简单替换代码如下图所示
使用
TextField
、Icon
和状态管理,可以创建具有搜索和清除功能的搜索框,是不是更加方便了呢
9. ScrollView
ScrollView在XML中用于滚动一个或多个子视图,以便在屏幕上显示超出可见区域的内容。在Compose中,可以使用LazyColumn来实现类似的滚动效果,简单替换代码如下所示
LazyColumn
基本适用于常见的滚动列表,它会根据需要延迟加载内容
10. ProgressBar
ProgressBar在XML中用于显示加载进度,通常用于等待长时间操作或网络请求。在Compose中,可以使用LinearProgressIndicator来创建加载进度条,简单替换代码如下图所示
使用
LinearProgressIndicator
可以轻松创建加载进度条,并使用状态管理来控制进度
11. WebView
WebView在XML中用于显示Web页面或嵌入网页内容。在Compose中,可以使用AndroidView来嵌入WebView,简单替换代码如下所示
创建一个嵌入的WebView,并加载指定的URL
12. Switch
Switch在XML中用于创建开关按钮,通常用于切换某个设置或状态。在Compose中,可以使用Switch组件来实现类似的开关按钮,简单替换代码如下所示
13. DrawerLayout
DrawerLayout在XML中用于创建抽屉式布局,通常用于侧边栏导航。在Compose中,可以使用ModalDrawer来实现侧边栏效果,简单替换代码如下图所示
使用
ModalDrawer
可以实现抽屉式布局,可以控制抽屉的状态并自定义抽屉和内容
以上就是一些常见的组件简单替换方案,具体的要根据项目需求,选择适合的Compose布局元素和修饰符,以满足我们的UI设计需求
最后想说的话
目前来说,国内Compose并没有完全替代XML传统布局,但将XML布局转换为Compose无疑是一项重要的工作,它可以带来许多好处,包括更好的可维护性、更高的开发效率和更好的用户体验。随着项目版本迭代,通过逐步迁移,我们可以逐渐引入Compose,并在不中断现有功能的情况下实现无缝衔接。
作者:RainyJiang
链接:https://juejin.cn/post/7288151382533390395
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。