Jetpack Compose
前言
Jetpack Compose是在2019 Google i/O大会上发布的新的库。Compose库是用响应式编程的方式对View进行构建,可以用更少更直观的代码,更强大的功能,提高开发速度(谷歌自己说的)。
Compose 并不是像 RecyclerView、ConstraintLayout 这种做了一个或者几个高级的 UI 控件,而是直接抛弃了我们写了 N 年的 View 和 ViewGroup 那一套东西,从上到下设计了一整套全新的 UI 框架。也就是说,它的渲染机制、布局机制、触摸算法以及 UI 的具体写法,全都是新的。
一 . Compose的特性
Compose库是声明式UI, 传统的View/Layout是命令式UI.
- 声明式UI: 只需要把界面给「声明」出来,而不需要手动更新.
- 命令式UI: 数据发生了改变,得手动用代码去把新数据更新到界面.
延伸:
- Compose的自动更新是通过mutableStateOf实现的.利用 Kotlin 的 Property Delegation 属性委托来实现的. Compose使用了大量的 Kotlin 特性,这也就回答了为什么Compose 只能用 Kotlin写.
- Android本身也有支持数据自动更新的库Data Binding, 区别在于功能范围: compose不止数据, 界面的任何内容,包括界面结构,布局等都可以自动更新.
就是由于这种机制的改变给界面开发带来的灵活性和性能的提升是非常大的.
二 . 开发环境
Jetpack Compose是从Android Studio 4.2开始支持的,所以需要通过4.2(现在是canary版本)创建新的项目或者添加导入库。
此时模块中的build.gradle文件会新增下列的库的依赖。
dependencies {
...
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
...
}
还有在模块的build.gradle文件中新增下列的设置。
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion '1.4.30'
}
三 . UI相关使用
3.1 @Compose
所有关于构建View的方法都必须添加@Compose
的注解才可以。被@Compose
的注解的方法只能在同样被@Comopse
注解的方法中才能被调用。
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
3.2 @Preview
加上@Preview注解的方法可以在不运行App的情况下就可以确认布局的情况。
@Preview的注解中比较常用的参数如下:
- name: String: 为该Preview命名,该名字会在布局预览中显示。
- showBackground: Boolean: 是否显示背景,true为显示。
- backgroundColor: Long: 设置背景的颜色。
- showDecoration: Boolean: 是否显示Statusbar和Toolbar,true为显示。
- group: String: 为该Preview设置group名字,可以在UI中以group为单位显示。
- fontScale: Float: 可以在预览中对字体放大,范围是从0.01。
- widthDp: Int: 在Compose中渲染的最大宽度,单位为dp。
- heightDp: Int: 在Compose中渲染的最大高度,单位为dp。
上面的参数都是可选参数,还有像背景设置等的参数并不是对实际的App进行设置,只是对Preview中的背景进行设置,为了更容易看清布局。
@Preview(showBackground = true, name = "Home UI", showDecoration = true)
@Composable
fun DefaultPreview() {
MyApplicationTheme {
Greeting("Android")
}
}
在IDE演示: 右上角有Code,Split,Design三个选项; 还有Build&Refresh.
3.3 setContent
setContent的作用是和View/Layout中的setContentView是一样的。
setContent的方法也是有@Compose注解的方法。所以,在setContent中写入关于UI的@Compopse方法,即可在Activity中显示。
@override
fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeDemoTheme {
Greeting("Android")
}
}
}
3.4 *Theme
在创建新的Compose项目时会自动创建一个项目名+Theme
的@Compose
方法。 我们可以通过更改颜色来完成对主题颜色的设置。 生成的Theme方法的代码如下。
private val DarkColorPalette = darkColorPalette( primary = purple200, primaryVariant = purple700, secondary = teal200 )
private val LightColorPalette = lightColorPalette( primary = purple500, primaryVariant = purple700, secondary = teal200
/* Other default colors to override background = Color.White, surface = Color.White, onPrimary = Color.White, onSecondary = Color.Black, onBackground = Color.Black, onSurface = Color.Black, */ )
@Composable
fun JetpackComposeDemoTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme( colors = colors, typography = typography, shapes = shapes, content = content )
}
在JetpackComposeDemoTheme
里面的所有UI方法都会应用上述主题中指定的颜色。
3.5 Modifier
Modifier是各个Compose的UI组件一定会用到的一个类。它是被用于设置UI的摆放位置,padding等信息的类。以下介绍会经常用到的。
- padding 设置各个UI的padding。padding的重载的方法一共有四个。
- Modifier.padding(10.dp) // 给上下左右设置成同一个值
- Modifier.padding(10.dp, 11.dp, 12.dp, 13.dp) // 分别为上下左右设值
- Modifier.padding(10.dp, 11.dp) // 分别为上下和左右设值
- Modifier.padding(InnerPadding(10.dp, 11.dp, 12.dp, 13.dp))// 分别为上下左右设值
这里设置的值必须为Dp,Compose为我们在Int中扩展了一个方法dp,帮我们转换成Dp。
- plus 可以把其他的Modifier加入到当前的Modifier中。
- Modifier.plus(otherModifier) // 把otherModifier的信息加入到现有的modifier中
- fillMaxHeight,fillMaxWidth,fillMaxSize 类似于match_parent,填充整个父layout。
- Modifier.fillMaxHeight() // 填充整个高度
- width,heigh,size 设置Content的宽度和高度。
- Modifier.width(2.dp) // 设置宽度
- Modifier.height(3.dp) // 设置高度
- Modifier.size(4.dp, 5.dp) // 设置高度和宽度
- widthIn, heightIn, sizeIn 设置Content的宽度和高度的最大值和最小值。
- Modifier.widthIn(2.dp) // 设置最大宽度
- Modifier.heightIn(3.dp) // 设置最大高度
- Modifier.sizeIn(4.dp, 5.dp, 6.dp, 7.dp) // 设置最大最小的宽度和高度
- gravity 在Column中元素的位置。
- Modifier.gravity(Alignment.CenterHorizontally) // 横向居中
- Modifier.gravity(Alignment.Start) // 横向居左
- Modifier.gravity(Alignment.End) // 横向居右
- rtl, ltr 开始布局UI的方向。
- Modifier.rtl // 从右到左
- Modifier.ltr // 从左到右
Modifier的方法都返回Modifier的实例的链式调用,所以只要连续调用想要使用的方法即可。
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!", modifier = Modifier.padding(20.dp).fillMaxSize())
}
3.6 Column,Row
正如其名字一样,Column和Row可以理解为在View/Layout体系中的纵向和横向的ViewGroup。
需要传入的参数一共有四个。
- Modifier 用上述的方法传入已经按需求设置好的Modifier即可。
- Arrangement.Horizontal, Arrangement.Vertical Row传入Arrangement.Horizontal,Column传入Arrangement.Vertical。
这些值决定如何布置内部UI组件,可传入的值为Center, Start, End, SpaceEvenly, SpaceBetween, SpaceAround。
重点解释一下SpaceEvenly, SpaceBetween, SpaceAround。
- SpaceEvenly:各个元素间的空隙为等比例。
- SpaceBetween:第一元素前和最后一个元素之后没有空隙,所有空隙都按等比例放入各个元素之间。
- SpaceAround:把整体中一半的空隙平分的放入第一元素前和最后一个元素之后,剩余的一半等比例的放入各个元素之间。
- Alignment.Vertical, Alignment.Horizontal 给Row传入Alignment.Vertical,为Column传入Alignment.Horizontal。
使用方法和Modifier的gravity中传入参数的用法是一样的,这里就略过了。
- @Composable ColumnScope.() -> Unit 需要传入标有@Compose的UI方法
Column {
Row(modifier = Modifier.ltr.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceAround, verticalGravity = Alignment.Top) {
// ..,...
}
四 控件的使用
4.1 Text
Text就是之前一直使用的TextView。 Text也是@Compose注解的方法,所以也需要在@Compose方法中调用。
- text : String: 设置所需要显示的文字。
- modifier : Modifier: 设置关于Text位置信息的Modifier。关于Modifier的使用方法查看第一篇。
- color : Color: 默认是Color.Unset。可以创建Color对象,Color(0xFF000000)。也可以直接使用默认已预置的颜色,如Color.Black。
- fontSize : TextUnit: 设置文字的大小,默认是TextUnit.Inherit,可以用TextUnit.Sp(10)方式设置。
- fontStyle : FontStyle: 设置字体类型,默认是null。可以设置正常字体FontSytle.Normal和斜体FontStyle.Italic。
- letterSpacing : TextUnit: 设置字符间距,默认是TextUnit.Inherit,可以用TextUnit.Sp(10)方式设置。
- textDecoration : TextDecoration: 设置删除线和下划线,默认是null。
- 删除线TextDecoration.LineThrough,
- 下划线TextDecoration.UnderLine,
- 没有任何装饰TextDecoration.None。
- textAlign : TextAlign: 设置文本对齐方式,默认是null。
- 左对齐TextAlign.Left,
- 右对齐TextAlign.Right,
- 中间对齐TextAlign.Center,
- 拉伸填充整个容器TextAlign.Justify,
- 还有TextAlign.Start和TextAlign.End就不解释了。
- style : TextStyle : 设置TextStyle,默认是currentTextStyle()。关于TextStyle以后再讲。
- maxLine : :Int: 设置Text最大行数,默认是Int.MAX_VALUE。
- onTextLayout : (TextLayoutResult) -> Unit: 设置一个回调,当新的Text Layout已经被计算出来,就会执行这个回调
Text(
text = "Hello $name!",
modifier = Modifier.padding(10.dp).gravity(Alignment.CenterVertically),
color = Color.Blue,
textAlign = TextAlign.End,
textDecoration = TextDecoration.LineThrough,
onTextLayout = {},
fontStyle = FontStyle.Italic,
maxLines = 1
)
4.2 Button
- Buton就是之前一直使用的ButtonView。 Buttom也是@Compose注解的方法。
- onClick : () -> Unit: 当按钮被点击时,会被调用。
- modifier : Modifier: 设置关于Text位置信息的Modifier。关于Modifier的使用方法查看第一篇。
- enabled : Boolean: 设置按钮的有效性,默认是true。
- elevation : Dp: 调整按钮在Z轴方向上的高度,默认是2.dp。
- disabledElevation : Dp: 调整按钮无效时在Z轴方向上的高度,默认是0.dp。
- shape : Shape: 调整按钮的样子,默认是MaterialTheme.shapes.small。可设置的样子如下。
- RoundedCornerShape: 设置圆角矩形的样式,
- CircleShape: 可设置圆形的样式,
- CutCornerShape: 可设置切角样式,
- border : Border: 设置按钮的外边框,默认是null。
- Border(size: Dp, color: Color) 适合一种颜色的外边框
- Border(size: Dp, brush: Brush)。实现渐变色的外边框
- backgroundColor : Color: 设置按钮的背景颜色,默认是MaterialTheme.colors.primary。
- disabledBackgroundColor : Color: 设置当按钮无效时的背景颜色,默认是Button.defaultDisabledBackgroundColor。
- contentColor : Color: 设置按钮的内容颜色,上面一系列的示例图中有黄色Android字就是因为设置了contentColor为黄色。
- disabledContentColor : Color: 设置当按钮无效时的内容颜色。
- padding : InnerPadding: 设置按钮的InnerPadding, 使用方法如下。padding = InnerPadding(start = 1.dp, top = 2.dp, end = 3.dp, bottom = 4.dp)
- text : @Composable () -> Unit: 设置Button内的布局,需要传入@Compose方法。
Button(
text = { Text(text = name) },
onClick = {},
modifier = Modifier.padding(12.dp),
backgroundColor = Color.Green,
contentColor = Color.Yellow,
elevation = 10.dp,
border = Border(2.dp, Color.Red)
)
4.3 OutlinedButton
OutlinedButton是特殊的Button, 是有外边框的按钮。设置方法跟上述一致。
4.4 TextButton
TextButton是特殊的Button, 是有外边框的按钮。设置方法跟上述一致。
4.5 Image
- Image就是之前一直使用的ImageView。 Image也是@Compose注解的方法。
- asset : VectorAsset 需要传入用来显示图片的VetorAsset。也可以传入库本本身自带的Icon,Icons.Filled.Home。
- modifier : Modifier 设置关于Text位置信息的Modifier。关于Modifier的使用方法查看第一篇。
- aligment : Aligment 需传入Alignment.Vertical,为Column传入Alignment.Horizontal。
- contentScale : ContentScale 这里是需要设置图片的显示模式,默认是ContentScale.Inside。详细介绍如下。
- ContentScale.Inside: 如果图片大于设置的图片显示区域,则会按比例缩小,如果是小于则不进行任何操作。
- ContentScale.Crop: 保持长宽比的情况下,按比例缩小或放大使图片大于等于图片的显示区域,使显示区域全部显示图片。
- ContentScale.Fit: 保持长宽比的情况下,按比例缩小或放大使图片小于等于图片的显示区域。
- ContentScale.FillHeight: 保持长宽比的情况下,使图片的高度等于图片显示区域的高度。
- ContentScale.FillWidth: 保持长宽比的情况下,使图片的长度等于图片显示区域的长度。
- alpha : Float 设置透明度,默认是1.0f。
- colorFilter : ColorFilter 可以设置颜色滤镜,这里就不具体展开了。
注意:第一参数asset : VectorAsset以外,作为代替还可以设置ImageAsset或者Painter。
- VectorAsset对象时可以使用vectorResource(resoureceId)方法来传入文件。
- ImageAsset对象时可以使用imageResource(resoureceId)方法来传入文件。
- Painter就不展开介绍了。
Image(
asset = vectorResource(id = R.drawable.ic_mllogosvg),
modifier = Modifier.ltr.padding(10.dp),
alignment = Alignment.Center
)
4.6 Spacer
Spacer就是之前一直使用的Space。Spacer也是@Compose注解的方法。需要显示空白区域时,可以使用Spacer。
- modifier : Modifier 设置关于Text位置信息的Modifier。关于Modifier的使用方法查看第一篇。
Spacer(Modifier.preferredWidth(16.dp))
4.7 Surface
想为一个我们自定义的组件添加背景颜色时,我们就需要用到Surface。
- modifier: Modifier: 为Surface设置modifier,第一篇中有讲述,这里就略过。
- shape: Shape: 为Surface设置形状,默认是RectangleShape。第二篇有讲述,这里就略过。
- color: Color: 为Surface设置颜色,也是为这一片区域设置背景颜色,默认是MaterialTheme.colors.surface。这里的默认值是在Theme的ColorPalette中设置的surface值。
- contentColor: Color: 为Surface中的Text设置颜色。在Text中没有设置颜色时,才会被设置成contentColor的颜色。当contentColor没有被设置任何值时,会按下列的逻辑设置。
1. 当Surface中有设置Color时,如果Color的颜色是ColorPalette的颜色,则contentColor会设置成与其搭配的默认颜色。如果不是则设置成Theme中的onSurface的颜色。有一个特例,当设置的Color颜色和Theme中的Surface的颜色一样时,contentColor会被设置成onSurface颜色。
2. 当Surface中没有设置Color时,则直接设置成Theme中的onSurface的颜色。 - border: Border?: 为Surface设置外边框,默认是是null。关于border在第二篇的Button中有讲述,所以就略过了。
- elevation: Dp: 为Surface设置在Z轴方向上的高度。默认是0.dp。
- content: @Composable () -> Unit: 为Surface设置内容布局,需要传入@Compose方法。
Surface(modifier = Modifier.padding(4.dp),color = Color.Gray) {
Column {
Text(modifier = Modifier.gravity(Alignment.CenterHorizontally),text = "MOON")
Image(modifier = Modifier.size(150.dp),asset = vectorResource(id = R.drawable.ic_mllogosvg))
}
}
4.8 LazyColumnFor, LazyRowFor
从名字就可以知道一个是横向显示,一个是纵向显示的组件。这个组件类似于我们经常使用的RecyclerView。
- items: List<T>: 传入需要显示的数据。
- modifier: Modifier: 设置modifier,第一篇中有讲述,这里就略过。
- itemContent: @Composable (T) -> Unit: 传入每一项数据需要显示的布局。不要忘了需要是@Compose方法。其中方法的传参为items中的数据,可以利用它在布局中显示数据
LazyColumnFor(items = createData()) {
Card(
elevation = 3.dp,
modifier = Modifier.padding(10.dp).fillMaxSize().heightIn(40.dp),
border = Border(2.dp, Color.LightGray)
) {
Column(
horizontalGravity = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = it.toString(), modifier = Modifier.padding(10.dp,5.dp))
}
}
}