拿来吧你,Compose尝鲜初体验!

3,702 阅读7分钟

前言

Compose 正式版在七月份的尾巴如期而至,我也写了一份 Compose 版本的项目,Demo其实比较简单。

结构

地址:github.com/mCyp/Hoo。

这已经是它的第三个版本:

  1. 第一版:Kotlin + Jetpack
  2. 第二版:Flutter
  3. 第三版:Compose + Jetpack

还记得去年 Compose 推出的时候,我就在团队内部分享了一次 Compose,当时为了展示一个 Preview 的功能,差点卡到下不了台。

麻了

其实写代码的时候速度还可以,但是投屏时属实有些尴尬。

一、技术栈使用

Hoo 功能虽小,但五脏俱全。

技术栈

整个项目的技术栈就是 Compose + Jetpack,由于 Google 大力推行 Android Jetpack,使得我们以后开发 Android 的技术栈越来越清晰,以后的趋势可能是掌握好 Jetpack 就可以帮助我们轻松的开发应用。

在使用 Compose 的时候,发现 Lottie 对 Compose 的支持速度还挺快,于是毫不犹豫的使用了 Lottie。

收藏效果

效果还不错~

二、入门必看

官方文档的地址: 官方入门

官方的Sample: Github Samples

官方页面

这是我在官网截的图,从图中来看,入门教程支持中文,目录结构也比较清晰,中文开发者入门还是比较轻松的。

‘渔’有了,很多人还有诸如以下的疑惑:

  • 为什么要用 Compose 代替 Android 原有的 View 结构?
  • 什么是声明式编程?
  • Compose开发有什么优缺点?

我来为大家一一解答。

1. xml构建UI之痛

首先,我们得明确一点,传统的 Android View 结构是基于面向对象。

从我们开始写安卓开始,我就使用 xml 构建 UI,等进入 Activity 或者 Fragment 的生命周期,系统再帮我们解析 xml 文件,一层层的搭建 Android View 结构树,等到使用的时候,会有两个问题:

第一,findViewById 这种方式查询 View 比较低效,每次都需要通过遍历 Android View 树获取指定的View。

第二,对于自定义View来说,我们可能需要管理一大堆的 get 和 set 方法,来管理 View 的各种状态,在一些场景下,手动管理各种 View 的 add 和 remove 也比较麻烦。所以,一些初中级的 Android 开发谈起自定义View总是眉头紧皱。

既然苦于 Android 既有 View 结构久矣,那 Google 自然就做了顺水推舟的事,Compose 也就应运而生。

2. 什么是声明式编程?

过去几年,我们在 Android 领域总是听到关于 声明式UI 的动态,先有大哥 Flutter,后有 Compose 小弟,为什么整 Android 行业都开始向 声明式UI 转变了呢?

也许从代码中找到一些答案,比如我们要去构建一个如下界面,并要求,点击第一个 Button,移除第二个 Button:

欢迎页

1.传统写法

构建xml布局:

<LinearLayout>
    <ImageView/>
    <TextView/>
    <Button/>
    <Button/>
</LinearLayout>

之后在 Activity 中创建各种 View 对象,通过命令式写法操作这些对象去设置图片,文字和点击事件等。当点击第一个 Button,我们还需要获取外层的 LinearLayout 对象,动态的 remove 第二个子 Button。

2.Compose写法

Compose 是一种声明式UI,你需要把界面描述出来,即使界面结构发生变化,你也不需要操纵原来的View,你需要做的是描述状态发生变化后的界面是什么样子的,以代码为例:

@Composable
fun WelcomePage(){
    val state = remember{ mutableStateOf(true)} // ... 获取数据
    Column(){
        Image(...)
        Text(...)
        Button(...) // 点击的时候设置state
        if(state.value){
            Button(...)
        }
    }
}

上面的代码中,很清晰的告诉我们,这是一个 Column,对应着原来的 LinearLayout,里面有 Image、Text 和 Button,当状态 state 的值是 true 的时候,会展示另外一个 Button。状态 state 的修改是通过第一个 Button 的点击事件设置的,这里我没有写出来。

Compose 中的重组意味着重建,当 State 发生变化时,描述UI的 @Compose 方法会发生重组。用一张图来描述添加 View 的情况:

添加子View

当然,整个界面重组毕竟是个很高昂的操作,对应着时间、计算能力和电池的消耗,Compose 当然做了优化,只会对必要 @Compose 方法进行重组。比如,上述的 state 发生变化的时候,只会重建 ViewGroup 和 Text2。

三、为什么要选择Compose?

声明式 UI 的大哥 Flutter 已经出道很久了,再学习 Compose 还有意义吗,Flutter 还是 Compose?

我先抛出我的结论:

  • 如果你想运用到实际的生产环境中,Flutter 肯定是更好的选择,因为更多的人帮你踩过了坑。
  • 如果你想 Kotlin 一把梭,只是学习尝鲜,结合 Android Jetpack,Compose 可以很好的作为你的技术储备。

简单的聊聊 Compose 中还不错的地方。

1. Android开发习惯的继承

相信很多同学都有这样的习惯:

  • 使用 Kotlin 开发
  • 必须协程
  • 复杂的布局会使用 ConstraintLayout
  • ...

是的,这些东西我们依然在 Compose 中运用,从而降低我们的上手难度。

2. Android Jetpack 的支持

在 Compose 刚刚发布的时候,Android Jetpack 中的很多其他库都第一时间给予了 Compose 支持,从而丰富了 Compose 的开发生态。

目前,能够直接在 Compose 上使用的 Jetpack 库有:

  1. Navigation
  2. Paging
  3. ViewModel
  4. LiveData
  5. hilt
  6. lifecycle

理论上来讲,Android Jetpack 上跟 UI 不相关的库 Compose 应该都是支持的,在我写的 Hoo 中,就使用了Paging、Navigation、ViewModel 和 LiveData等 Android Jetpack 库,再有协程和 Kotlin 的加持,整个开发过程中轻松不少!

3. 更少的代码

Compose 可以使我们更加专注于 UI 的开发,声明式UI 可以显著的减少方法数和包体积。在谷歌官方的 《Jetpack Compose 使用前后对比》 一文说道:

Tivi应用在使用了 Compose 后,我们发现 APK 大小缩减了 41%,方法数减少了 17%

其实这些都是可以预见的,比如更加简单的动画和触摸事件的 Api。

4. Preview

Compose支持代码的Preview,如图:

Preview

Compose 代码写完后,可以直接在右边预览,但是更新速度差点意思,不如 Flutter 的热重载方便。

5. 其他

其他的一些点可能就跟 Flutter 有点像了。

  • Compose 的主题原生支持黑夜模式,开发者定制主题的时候提供两套颜色即可,想起之前,起点读书支持黑夜模式可是花了很大的功夫。

  • 通过 Scaffold,可以轻松集成很多 Material 组件,比如 Topbar、FloatingActionButton 和 BottomNavigationBar 等,这些都可以帮助我们节省出不少的时间。

  • 另外,在 Compose 中不能轻松实现效果的时候,借助于 AndroidView,可以去调用Android原生View。

四、吐槽

当然了,Compose 目前还有一些槽点。

1. 生态不丰富

Compose 作为刚出来的 UI 框架,虽然 Android Jetpack 第一时间给予了支持,但生态不丰富这一点是毋庸置疑的。

一些非基础的常用UI,Google 给出了一些解决方案,比如 【accompanist】,它可以帮助你解决部分常用的库。

如果没有你想要的轮子,你只能选择自己造或者引用 Android 原生控件。不过这从侧面也说明了一些机会,当你觉得缺少什么的时候,写出好的开源库的机会来临了~

2. 学习资源少

在学习 Flutter 的时候,遇到某种效果,可能谷歌一下,就有答案了。

但在现阶段的 Compose 中,大概率要自己动手~

3. 导航

作为官方指定的导航 - Navigation,在非 Compose 代码中集成是支持页面之间的过渡动画的,在 Compose 中目前还是不支持的(现在看了一下,有支持的库了)。

Navigation 文档中说明是可以记录页面状态的,但就实际使用而言,有的页面记录状态还是有问题的,比如,纯粹使用 Paging + LazyColumn,当页面切换时,会记录当前页面位置,当加上 Header 就不行了。

当然了,也有可能是我的使用姿势问题。

最后

第一次写 Compose 项目,难免会有小问题,欢迎指正,感谢~

参考文章:

《从函数式编程到声明式UI》
《声明式 UI?Android 官方怒推的 Jetpack Compose 到底是什么》
官方文档