简介

Jetpack Compose 是用于构建native UI的新方式,写法跟Flutter非常相似,对Flutter有了解的同学可以很快上手。
官网:https://developer.android.com/jetpack/compose
官方demo :https://github.com/android/compose-samples
官方的介绍: https://developer.android.com/jetpack/compose/setup

环境及版本

最低支持Android API 21,即5.0版本,必须使用kotlin语言,最低使用Android Studio 4.0 版本。
Jetpack Compose 目前处于实验阶段,现在是0.1-dev2,到1.0正式版估计还要一年时间。
后续版本可能会加入更多kotlin的特性,丰富动画等其他性能问题。

关于如何在现有项目中使用:
https://developer.android.com/jetpack/compose/setup#add-compose

怎么使用?

在AS 4.0中直接新建一个空的Compose项目,会有一个示例代码:
Android Jetpack Compose总结_第1张图片
在函数前加@Compose注解,就可以返回一个类似Flutter中的Widget的UI
@Compose注解的函数可以相互调用,这些函数会被插件编译处理,所以如果一个函数不是生成UI的,那么不要用此注解。
@Preview注解,可以在右边实时预览,改动函数后,刷新一个预览即可,添加该注解的外层函数不能有参数,但是里面可以嵌套一个带参数的函数来预览。可以在@Preview后面添加一个名字,如:@Preview("Text preview")

关于Column 和Row 的概念跟Flutter中一样,包括主轴和副轴的概念大小如mainAxisSize和对齐方式如crossAxisAlignment,一段代码示例:

@Composablefun MyScreenContent(    names: List<String> = listOf("Android", "there"),    counterState: CounterState = CounterState()) {    Column(crossAxisAlignment = CrossAxisAlignment.Center      crossAxisSize = LayoutSize.Expand,        mainAxisSize = LayoutSize.Expand) {        for (name in names) {            Greeting(name = name)            Divider(color = Color.Black)        }        Divider(color = Color.Transparent, height = 32.dp)        Counter(counterState)    }}@Preview("MyScreen preview")@Composablefun DefaultPreview() {    MyApp {        MyScreenContent()    }}

可以使用HeightSpacer(24.dp)或者WeightSpacer(24.dp)来直接添加一个宽高间隔

按照官方的建议,我们可以把UI拆分成多个小的Compose函数,每个函数其实最终会被插件编译生成一个View,然后可以复用这些Compose函数,

@Composablefun MyScreenContent(    names: List<String> = listOf("Android", "there"),    counterState: CounterState = CounterState()) {    Column(modifier = ExpandedHeight, crossAxisAlignment = CrossAxisAlignment.Center) {        Column(modifier = Flexible(1f), crossAxisAlignment = CrossAxisAlignment.Center) {            for (name in names) {                Greeting(name = name)                Divider(color = Color.Black)            }        }        Counter(counterState)    }}

在Column中,可以对参数modifier设置ExpandedHeight,类似于设置高度match_parent的意思,宽度同理。

关于如何使用Theme和自定义Theme

MaterialTheme中有很多颜色和字体样式,在外层包裹上MaterialTheme后,可以在内部的Compose函数中使用主题数据,如:style = +themeTextStyle { h1 }

@Composablefun Greeting(name: String) {    Text (        text = "Hello $name!",        modifier = Spacing(24.dp),        style = +themeTextStyle { h1 }        color = +themeColor { surface }    )}

通过使用copy函数可以在现有的一个主题上修改某一个属性值,如:

textStyle = (+themeTextStyle { body1 }).copy(color = Color.Yellow)

自定义Theme

import androidx.compose.Composable@Composablefun CustomTheme(children: @Composable() () -> Unit) {    // TODO }
import androidx.compose.Composableimport androidx.ui.core.CurrentTextStyleProviderimport androidx.ui.graphics.Colorimport androidx.ui.material.MaterialColorsimport androidx.ui.material.MaterialThemeimport androidx.ui.text.TextStyleval green = Color(0xFF1EB980.toInt())val grey = Color(0xFF26282F.toInt())private val themeColors = MaterialColors(    primary = green,    surface = grey,    onSurface = Color.White)@Composablefun CustomTheme(children: @Composable() () -> Unit) {    MaterialTheme(colors = themeColors) {        val textStyle = TextStyle(color = Color.Red)        CurrentTextStyleProvider(value = textStyle) {            children()        }    }}

Effects和memo

memo的作用:

1. 在recompositions(即该UI组件内部的Model数据变化时,该UI组件就会重新构建)的时候保存状态值,如下代码:

@Composablefun MyScreenContent(    names: List<String> = listOf("Android", "there"),    counterState: CounterState = CounterState()) { ... }

上面的代码有一个问题,再重新构建的时候,原来的counterState数值就会丢失,每次都是一个新的counterState对象。
按照下面使用memo修改后,就可以解决问题:

@Composablefun MyScreenContent(    names: List<String> = listOf("Android", "there"),    counterState: CounterState = +memo { CounterState() }) { ... }

2. 在重组时,记住内部的一些计算结果,防止多次重复计算

如果在合成的中间需要进行计算,而又不想在每次重新组合函数时都进行计算,则可以记住该计算,即使重新组合了Composable函数,该计算也不会再次执行。

@Composablefun Greeting(name: String) {    val formattedName = +memo { name.substringBefore(" ").toUpperCase() }    Text (        text = "Hello $formattedName!",        modifier = Spacing(24.dp),        style = +themeTextStyle { h3 }    )}@Preview@Composablefun DefaultPreview() {    MaterialTheme {        Greeting("Android 10")    }}

比如这里的formattedName计算过程,在使用memo后,就不会重复计算,但是这样写有个bug,如果第二次调用时传入来了另外一个参数,那么由于memo复用原来的结果,就会导致bug,所以,对于需要修改的参数,可以以如下的方式来使用memo:

@Composablefun Greeting(name: String) {    val formattedName = +memo(name) { name.substringBefore(" ").toUpperCase() }    Text (        text = "Hello $formattedName!",        modifier = Spacing(24.dp),        style = +themeTextStyle { h3 }    )}

@Model注解

model注解标记一个数据类之后,在Compose函数中可以直接监听到数据变化,自动更新显示,
如:
定义:

@Model class CounterState(var count: Int = 0)

使用:

@Composable fun Counter(state: CounterState) {    Button(        text = "I've been clicked ${state.count} times",        onClick = {            state.count++        }    )}

状态提升、 数据流向下传递、事件流向上传递

@Modelclass FormState(var optionChecked: Boolean)@Composablefun Form(formState: FormState) {    Checkbox(        checked = formState.optionChecked,        onCheckedChange = { newState -> formState.optionChecked = newState })}

在上面代码中,Checkbox的选中状态,在Checkbox和Form中都不保存,而改为由外部传入,原因是此时外部可能需要使用当前的状态值,那么由外部来创建并传递该参数到Compose函数中,这使得外部调用者提升了状态

⚠️注意:在可组合函数中,应该公开可能对调用函数有用的状态,因为这是可以使用或控制的唯一方法,称为状态提升。

状态提升的概念跟Flutter一样,后续应该也会像Flutter中的Provider、BLOC、或者Redux一样,推出相关的状态管理库,因为Compose + Model注解的方式,就是一种MVVM的思想,需要一种方便的数据状态管理的三方库来做这个事情。

关于数据流向: 父Composable函数可以控制其子数据。 子Compose UI不应从全局变量或全局数据存储中读取。Composable函数应仅接收所需信息,因此它们应尽可能简单,而不是调用父Composable函数可以提供的所有内容。

@Composablefun MultipleGreetings(user: User = +memo { User("name", "surname") }) {    Column {        Greeting("${user.firstName} ${user.lastName}")        Greeting("Android 10")        Button(text = "Change name", onClick = {            user.firstName = "Changed"            user.lastName = "New surname"        })    }}@Composablefun Greeting(name: String) {    val formattedName = +memo(name) { name.substringBefore(" ").toUpperCase() }    Text (        text = "Hello $formattedName!",        modifier = Spacing(24.dp),        style = +themeTextStyle { h3 }    )}

比如上面代码中,Greeting从调用方Compose函数(MultipleGreetings)获取数据,作为参数传入,且Greeting只接收一个String,并不是整个User对象。

事件向上传递

事件通过lambda回调而不断往上。 当子Composable函数收到事件时,更改应传播回至关心该信息的Composable。

在我们的示例中,我们可以通过将Greeting的内容包装在以onClick侦听器为参数的Clickable函数(可在库中使用)中来使其可点击。 但是,Greeting是一个可重用的功能,它本身并不知道如何处理用户交互。 应该使用lambda将该信息从层次结构的底部(Greeting中的Clickable composable)传播到顶部的Composable函数,这些函数知道如何处理该信息,如以下示例所示:

@Composablefun MultipleGreetings(user: User = +memo { User("name", "surname") }) {    val onClick = {        user.firstName = "Changed"    }    Column {        Greeting("${user.firstName} ${user.lastName}", onClick)        Greeting("Android 10", onClick)        Button(text = "Change name", onClick = onClick)    }}@Composablefun Greeting(name: String, onClick: () -> Unit) {    val formattedName = +memo(name) { name.substringBefore(" ").toUpperCase() }    Clickable(onClick = onClick) {        Text (            text = "Hello $formattedName!",            modifier = Spacing(24.dp),            style = +themeTextStyle { h3 }        )    }}

Greeting通过调用父级作为参数传递的lambda告诉MultipleGreetings它被单击了。 如果您运行该应用程序,则可以看到在任何问候语文本上进行点击都会传播更改,并且顶部的Greeting实例将重新组合。

Android Jetpack Compose总结_第2张图片
Android Jetpack Compose总结_第3张图片
Data flow in Compose apps. Data flows down with parameters, events flow up with lambdas.


Compose和现有的View互操作

Compose写的函数可以用在xml中,Android现有的View也可以用Compose的方式来写,如:
Android Jetpack Compose总结_第4张图片
Android Jetpack Compose总结_第5张图片
总结: Compose借鉴了Flutter和Swift UI的编写方式,代码简洁,可以实时预览效果,截止到2019年11月19日,目前版本才为0.1,预计正式发布1.0后,会有更多功能更新,日常的一个小demo可以先使用Compose熟悉起来。

参考:
https://codelabs.developers.google.com/codelabs/jetpack-compose-basics/#0

牛逼!Android Jetpack Compose UI组件库最新进展,写法完全类似Flutter

Android Studio 4.0 最新进展,这几个新功能可太牛逼了!

更多相关文章

  1. android中 检查网络连接状态的变化,无网络时跳转到设置界面
  2. android实现标题栏、状态栏图标文字颜色及背景动态变化
  3. 修改状态栏颜色和状态栏字体颜色
  4. 【总结备用】Android监听网络状态实现(BroadcastReceiver + Serv
  5. android 如何实现EditText从不可编辑状态变成可变成可编辑状态
  6. android如何判断服务是否正在运行状态
  7. android 状态栏提醒 Notification 的使用!
  8. Android沉浸式状态栏(包含 小米、魅族)
  9. android Toolbar的使用结合状态栏与返回键

随机推荐

  1. 类的封装、继承和多态
  2. PHP中操作任意精度大小的GMP扩展学习
  3. Scala 两数之和
  4. 前端异常监控和容灾
  5. Spring认证_什么是Spring GraphQL
  6. 简单入门PHP中的多字节字符串操作
  7. git的撤销、删除和版本回退
  8. KubeSphere 核心架构浅析
  9. 学习PHP中好玩的Gmagick图像操作扩展的使
  10. 怎么样优化数据库语句?