概览
Jetpack Compose 是用于构建原生 Android 界面的新工具包。它可简化并加快 Android 上的界面开发,帮助您使用更少的代码、强大的工具和直观的 Kotlin API,快速打造生动而精彩的应用。
为什么使用Compose
- 更少的代码
编写代码只需要采用 Kotlin,而不必拆分成 Kotlin 和 XML 部分。 - 直观
Compose 使用声明性 API,这意味着您只需描述界面,Compose 会负责完成其余工作。在 Compose 中,状态是显式的,并且会传递给相应的可组合项。这样一来,状态便具有单一可信来源,因而是封装和分离的。然后,应用状态变化时,界面会自动更新。 - 加快应用开发
借助全面的 Android Studio 支持以及实时预览等功能,您可以更快地迭代和交付代码。 - 功能强大
利用 Compose,您可以凭借对 Android 平台 API 的直接访问和对于 Material Design、深色主题、动画等的内置支持,创建精美的应用。
@Composable
fun JetpackCompose() {
Card {
var expanded by remember {
mutableStateOf(false)
}
Column(Modifier.clickable { expanded = !expanded }) {
Image(painterResource(R.drawable.jetpack_compose))
AnimatedVisibility(expanded) {
Text(
text = "Jetpack Compose",
style = MaterialTheme.typography.h2
)
}
}
}
}
Compose编程思想
- 声明式编程
命令式界面:使用 findViewById() 等方法遍历树,并通过调用 button.setText(String)、container.addChild(View) 或 img.setImageBitmap(Bitmap) 等方法更改节点。
声明式界面:声明性界面模型大大简化了与构建和更新界面关联的工程设计。 - 可组合函数
使用 Compose,您可以通过定义一组接受数据而发出界面元素的可组合函数来构建界面。
@Composable
fun Greeting(names: List<String>) {
for (name in names) {
Text(text = "Hello $name")
}
}
- 重组
@Composable
fun ClickSample(clicks: Int, onClick: () -> Unit) {
Button(onClick = onClick) {
Text(text = "clicked $clicks times")
}
}
每次点击该按钮时,调用方都会更新 clicks 的值。Compose 会再次调用 lambda 与 Text 函数以显示新值,此过程称为“重组”。不依赖于该值的其他函数不会进行重组。
重组是指在输入更改时再次调用可组合函数的过程。当函数的输入更改时,会发生这种情况。当 Compose 根据新输入重组时,它仅调用可能已更改的函数或 lambda,而跳过其余函数或 lambda。通过跳过所有未更改参数的函数或 lambda,Compose 可以高效地重组。
可组合函数可以按任何顺序执行
@Composable
fun ButtonRow() {
MyFancyNavigation {
StartScreen()
MiddleScreen()
EndScreen()
}
}
重组会跳过尽可能多的内容
/**
* Display a list of names the user can click with a header
*/
@Composable
fun NamePicker(
header: String,
names: List<String>,
onNameClicked: (String) -> Unit
) {
Column {
// this will recompose when [header] changes, but not when [names] changes
Text(header, style = MaterialTheme.typography.h5)
Divider()
// LazyColumn is the Compose version of a RecyclerView.
// The lambda passed to items() is similar to a RecyclerView.ViewHolder.
LazyColumn {
items(names) { name ->
// When an item's [name] updates, the adapter for that item
// will recompose. This will not recompose when [header] changes
NamePickerItem(name, onNameClicked)
}
}
}
}
/**
* Display a single name the user can click.
*/
@Composable
private fun NamePickerItem(name: String, onClicked: (String) -> Unit) {
Text(name, Modifier.clickable(onClick = { onClicked(name) }))
}
当 header 发生更改时,Compose 可能会跳至 Column lambda,而不执行它的任何父项。
此外,执行 Column 时,如果 names 未更改,Compose 可能会选择跳过 LazyColumn。
布局、控件
布局
Row & Column:
@Composable
fun ArtistCard(artist: Artist) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(/*...*/)
Column {
Text(artist.name)
Text(artist.lastSeenOnline)
}
}
}
Box:
@Composable
fun ArtistAvatar(artist: Artist) {
Box {
Image(/*...*/)
Icon(/*...*/)
}
}
主题
颜色:颜色在 Compose 中使用 Color 类(一个简单的数据存放类)进行建模。
val Red = Color(0xffff0000)
val Blue = Color(red = 0f, green = 0f, blue = 1f)
使用主题颜色:
Text(
text = "Hello theming",
color = MaterialTheme.colors.primary
)
排版:Compose 使用 Typography、TextStyle 和字体相关类来实现字型系统
Text(
text = "Subtitle2 styled",
style = MaterialTheme.typography.subtitle2
)
形状:Compose 使用 Shapes 类来实现形状系统
val Shapes = Shapes(
small = RoundedCornerShape(percent = 50),
medium = RoundedCornerShape(0f),
large = CutCornerShape(
topStart = 16.dp,
topEnd = 0.dp,
bottomEnd = 0.dp,
bottomStart = 16.dp
)
)
MaterialTheme(shapes = Shapes, /*...*/)
从主题检索形状:
Surface(
shape = MaterialTheme.shapes.medium, /*...*/
) {
/*...*/
}
列表
@Composable
fun SimpleList() {
// We save the scrolling position with this state that can also
// be used to programmatically scroll the list
val scrollState = rememberScrollState()
Column(Modifier.verticalScroll(scrollState)) {
repeat(100) {
Text("Item #$it")
}
}
}
文字
@Composable
fun ParagraphStyle() {
Text(
buildAnnotatedString {
withStyle(style = ParagraphStyle(lineHeight = 30.sp)) {
withStyle(style = SpanStyle(color = Color.Blue)) {
append("Hello\n")
}
withStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold,
color = Color.Red
)
) {
append("World\n")
}
append("Compose")
}
}
)
}
图形
@Composable
fun drawLineCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
val canvasWidth = size.width
val canvasHeight = size.height
drawLine(
start = Offset(x = canvasWidth, y = 0f),
end = Offset(x = 0f, y = canvasHeight),
color = Color.Blue,
strokeWidth = 5f
)
}
}
@Composable
fun drawCircleCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
val canvasWidth = size.width
val canvasHeight = size.height
drawCircle(
color = Color.Blue,
center = Offset(x = canvasWidth / 2, y = canvasHeight / 2),
radius = size.minDimension / 4
)
}
}
动画
var editable by remember { mutableStateOf(true) }
AnimatedVisibility(visible = editable) {
Text(text = "Edit")
}
手势
@Composable
fun ClickableSample() {
val count = remember {
mutableStateOf(0)
}
// content that you want to make clickable
Text(text = count.value.toString(),
modifier = Modifier
.clickable { count.value += 1 }
.pointerInput(Unit) {
detectTapGestures(
onPress = {},
onDoubleTap = {},
onLongPress = {},
onTap = {}
)
})
}