这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战
历时两年,Android 团队推出了全新的原生 Android 界面 UI 库——Compose。当然,Compose 也是属于 Jetpack 工具库中的一部分,官方宣称可以简化并加快 Android 上的界面开发,可以用更少的代码去快速打造生动而精彩的应用。1.0 版本就在上个月底刚刚发布,而且可以在生产环境中使用!不管咋样,先上手看一看!
1. 上手成本如何?
个人感觉,还行,有一定的学习成本。前提条件,对 Kotlin 语言熟悉,因为 Compose 都是用 Kotlin 语言开发实现的,对其他的 Jetpack 库熟悉就更好了。
Compose 可以和现有的工程项目进行互操作。比如,我们可以将 Compose UI 放到现有布局的 View 中,也可以将 View 放到 Compose UI 中。
作为 Jetpack 工具库的一部分,Compose 当然也可以十分方便地与 LiveDada、ViewModel、Paging 等工具一起整合,从而提高编码效率。
Compose 也提供了 Material Design 组件和主题的实现,同时还有简明的动画 API 可以让应用更加灵动,体验更好。
2. 官方广告概述
Google 毕竟憋了两年,怎么说也得有两把刷子的。Compose 是 Google 新推出的适用于 Android 的新式声明性界面工具包。个人理解的声明性的意思是:UI 的控件只需要我们一开始的时候声明创建出来,绑定了数据就可以了,后续的更新可以全部交给 Compose 处理。
Google 是考虑到现在的应用展示的绝大多数不是静态数据,更多的是会实时更新的。而现有的 xml 界面,更新比较复杂繁琐,很容易出现同步错误。并且软件维护的复杂性还会随着需要更新的视图数量而增长,为了解决这一问题,Google 才想完全舍弃原有的用 xml 写视图的方案,重新开发出 Compose 这一整套的解决方案。
Compose 首先会生成整个屏幕,然后仅仅执行必要的更改。它是将 State 状态转化成 UI 界面,并且会智能地跳过那些数据没有发生改变的控件,重新生成已经发生改变的控件,这一过程称之为重组(recomposition)。此外,Compose 布局模型不允许多次测量,最多进行两次测量就可算出各组件的尺寸。
3. 环境搭建
对 IDE 版本有要求,需要下载最新版的 Android Studio —— Android Studio Arctic Fox,目前是 2020 3.1 版本。这个版本在“新建项目”中支持选择 Compose 模板,并且有即时预览 Compose 界面等功能。
一般情况下,对于这种新的技术,我们都会先在主项目中的非核心功能进行实践,慢慢摸索,等到坑踩得差不多了,才会考虑将之前老的工程代码用新的方法重构。所以,Compose 也支持添加到现有的项目中进行使用。
3.1 配置 Kotlin 和 Gradle
需要确保项目中使用的 Kotlin 版本在 1.5.10 及以上。
还需要将应用的最低 API 级别设置为 21 或更高,即 Android 5.0 版本及以上。另外还需将 app 目录下的 gradle 文件中启用 Jetpack Compose,并设置 Kotlin 编译器插件的版本。
android { defaultConfig { ... minSdkVersion 21 // SDK 版本最低为 21 } buildFeatures { // Enables Jetpack Compose for this module compose true // 开启 Compose } ... // Set both the Java and Kotlin compilers to target Java 8. compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" } composeOptions { // 编译器插件版本设置 kotlinCompilerExtensionVersion '1.0.0-rc02' } }
3.2 添加工具包依赖项
官方文档上需要添加的依赖如下:
dependencies { implementation 'androidx.compose.ui:ui:1.0.0-rc02' // Tooling support (Previews, etc.) implementation 'androidx.compose.ui:ui-tooling:1.0.0-rc02' // Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.) implementation 'androidx.compose.foundation:foundation:1.0.0-rc02' // Material Design implementation 'androidx.compose.material:material:1.0.0-rc02' // Material design icons implementation 'androidx.compose.material:material-icons-core:1.0.0-rc02' implementation 'androidx.compose.material:material-icons-extended:1.0.0-rc02' // Integration with activities implementation 'androidx.activity:activity-compose:1.3.0-rc02' // Integration with ViewModels implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07' // Integration with observables implementation 'androidx.compose.runtime:runtime-livedata:1.0.0-rc02' implementation 'androidx.compose.runtime:runtime-rxjava2:1.0.0-rc02' // UI Tests androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.0.0-rc02' }
其实如果只是想上手看看效果,没必要添加 Integration with activities、ViewModels、observables 这些库。
4. 简单上手
Compose 核心内容就是可组合的函数,如同它的英文名称一样,将 UI 拆解成一个个可组合在一起的 Composable 函数,方便维护与复用。但是,可组合函数只能在其他的可组合函数的范围内调用。要使函数成为可组合函数,只需在该函数上方添加 @Composable 注解即可。其实可以直接把被 @Composable 注解的函数看成是一个 View。
@Composable 注解可告诉 Compose 编译器:此函数旨在将数据转换为界面。并且生成界面的 Compose 函数不需要返回任何内容,因为它们描述的是所需的屏幕状态,而不是构造界面的组件。
还有一个很强大的功能是,Compose 是支持在 IDE 中预览可组合函数的,只需要在 Composable 函数上再添加一个 @Preview 注解就可以了,限制条件是 @Preview 注解只能修饰一个无参的函数,所以,如果你要预览,就得保证你预览的函数无参,或者再用一个无参函数包起来:
// code 1 @Composable fun Greeting(name: String) { Text(text = "Hello $name!") } @Preview @Composable fun WrapperView() { Greeting("hahahaha") }
就这样子,你就可以在 IDE 中看到预览的效果了,甚至都没有执行入口! Compose 的 Hello World 代码也比较简单,只需要在 setContent 方法里添加需要展示的 Composable 函数即可:
// code 2 class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { // 设置显示内容,相当于 setContentView Greeting("Hello World!") } } }
4.1 Compose 布局初探
如果写过 Flutter,那么你会发现,Compose 的布局与 Flutter 类似。Column 可以将元素从上到下进行排列,类似于 LinearLayout 布局的 oritation 设置为 vertical。Row 就是将元素从左到右进行排列,类似于 LinearLayout 布局的 oritation 设置为 horizonal。还有 Box 堆叠布局,类似于 FrameLayout 布局,这里不再展开。下面是 Column 布局的一个简单例子,代码显示的效果如代码下方的图片所示:
// code 3 @Composable fun NewStory() { Column( // 许多对象都有这个 Modifier 属性,这个属性非常重要,这里是设置了 padding modifier = Modifier.padding(16.dp) ) { Image( painter = painterResource(id = R.drawable.header), contentDescription = null ) Text("今天天气好") Text(text = "郑州") Text(text = "July 2021") } }
这里要说下 contentDescription 属性,官方教学文档的原文是:
Note: You also need to provide a contentDescription for the image. The description is used for accessibility. However, in a case like this where the image is purely decorative, it's appropriate to set the description to null, as we do here.
意思是:我们需要为 image 提供一个 contentDescription 属性。这个属性用于可访问性(=。=?)。然鹅,如果这个 image 纯粹只是装饰作用,那么也可以像我们在这里设置的一样,设为 null。
懵逼脸。然后去源码看看这个到底是个啥?
*@param contentDescription text used by accessibility services to describe what this image *represents. This should always be provided unless this image is used for decorative purposes, *and does not represent a meaningful action that a user can take. This text should be *localized, such as by using [androidx.compose.ui.res.stringResource] or similar
意思是:" 这个属性是可访问性服务用于描述此图片代表的是什么。这个属性的信息应该都要提供,除非此图只是用于装饰的目的,或者并没有表示用户有特殊意义的操作。此外,属性的信息文本应该存放在本地资源中,如 res 目录下的 string 或类似的地方。"
额。。。还是有点懵,去网上看了下 ImageView 中的 contentDescription 属性,好像是为了方便视力有障碍的人群所设置的。反正绝大多数情况下可以忽略,如有实际用途,欢迎交流讨论。
此外,Compose 的布局还有很灵活的,还记得在 LinearLayout 布局中可以设置 weight 来控制填充父布局吗?在 Compose 也有类似的用法,直接上代码吧~
// code 4 @Composable fun MyScreenContent(names: List<String> = listOf("Android","there")) { Column(modifier = Modifier.fillMaxHeight()) { // 类似于 match_parent Column(modifier = Modifier.weight(1f)) { // 占满父布局剩余的高度空间 for (name in names) { Text(text = name) Divider(color = Color.Black) } } Button(onClick = { }) { Text(text = "这是第二个 Button") } } }
这个布局就是可以将 Button 放在父布局的底部位置,然后父布局剩余空间都会被内层的 Column 布局占满。
当然,Compose 可以轻松地遵循 Material Design 原则,因为可以直接在任何 Composable 函数外部用 MaterialTheme {} 包裹起来,这就可以使用 MaterialTheme 的属性了。包括字体样式、色值等。这里代码都比较简单,不再赘述。代码示例:gitee.com/xiuzhizhu/C…