声明式UI布局
声明式布局,到底是什么?
- 一种在构建MVVM业务逻辑阶段,就需要把所有情况定义好的UI布局,通过数据去推动页面的更新状态形式。(这是一种绝对的数据状态驱动UI展示逻辑,而不再是获取到数据之后,再去根据数据手动更新UI)
- 目前不仅是Jetpack Compose使用了这样的一个状态,iOS的SwiftUI,Google的Flutter Platform,还有Facebook搞的那个React Native,也都是这样的一种开发模式。
需要改变的思维
- 这与传统的把页面写好,然后通过最新数据去update不一样。Jetpack Compose存在一个概念,Android官方称之为重组。
- 我理解的重组:有点像我们构建好了一切的可能性,然后根据最新的数据状态去展示相应的UI。
- 重组范围:这个点也比较核心重要
A Sample
以一个滚动列表的布局为Sample,XML布局下面它的写法可能如下:
xml
<FrameLayout>
<RecyclerView
android:id="@+id/rv_list"/>
<ErrorLayout
android:id="@+id/error_layout"/>
<EmptyLayout
android:id="@+id/empty_layout"/>
</FrameLayout>
kotlin
class CommendFragment: Fragment() {
// ...... 省略ViewDataBinding、RecyclerView Adapter和ViewModel等等这些资源的实例化等等内容
override fun onViewCreate() {
viewModel.uiData.observe(this.viewLifecycleOwner) {
when (it) {
is Success -> {
binding.rvList.visible = View.VISIBLE
binding.errorLayout.visible = View.GONE
binding.emptyLayout.visible = View.GONE
rvListAdapter.setData(it.data)
}
is Failed -> {
binding.rvList.visible = View.GONE
binding.errorLayout.visible = View.VISIBLE
binding.emptyLayout.visible = View.GONE
}
is Empty -> {
binding.rvList.visible = View.GONE
binding.errorLayout.visible = View.GONE
binding.emptyLayout.visible = View.VISIBLE
}
}
}
}
}
而如果采用Jetpack Compose去构建这个页面,则页面如下展示
kotlin
@Composable
fun SuccessUI() {
// ...... SuccessUI
val viewModel = viewModels()
val data = viewModel.uiDateState.value as? Success ?: return
// Build the UI By data
}
@Composable
fun FailedUI() {
// ...... FailedUI
// Build the Failed UI
}
@Composable
fun EmptyUI() {
// ...... EmptyUI
// Build the Empty UI
}
@Composable
fun HostUI() {
val viewModel = viewModels()
val uiDataState = viewModel.uiDataState.value
when (uiDataState) {
is Success -> {
SuccessUI()
}
is Failed -> {
FailedUI()
}
is Empty -> {
EmptyUI()
}
}
}
- 而且,Jetpack Compose还有一个最重要的核心点,它的SuccessUI、FailedUI、EmptyUI是完全独立开的,如果是团队协作的过程中,完全可以由架构师、或者Feature Owner去构建好HostUI所决定的整个结构的接口或者空实现等内容,然后交由团队内部的其他成员去落实具体每一个UI页面的实现代码。
- 这一点,如果是使用XML去构建页面,过多的代码耦合在同一个文件,如果采用多人协作不同状态的UI,在发起PR的时候,极大可能会出现Merge Confirm的情况。而采用Jetpack Compose,可以将不同的UI放在不同的文件下,避免Merge Confirm的出现,在很大的程度上,是可以提高团队开发协作效率的。
目前Jetpack Compose存在的问题
控件不熟悉
对于传统的横纵向排放元素,直觉上使用LinearLayout配合weight权重或者ConstraintLayout的约束关系,可以完美解决这个问题,然而在Jetpack Compose,可能会一下子想不到使用什么样的控件去实现这个效果比较合适。
这个问题的本质,还是开发者对于一些控件能够实现的效果熟悉程度不足,多用就行。(Row和Column可以完美解决这个问题)
性能卡顿问题
我个人尝试性使用Jetpack Compose的LazyHorzontialColumn + Coil(一个适用于Jetpack Compose的图片加载库)去实现一个瀑布流的相册,然而实际对比传统的RecyclerView + Glide,出现图片加载速度慢、图片数量级上来之后出现掉帧等情况。