接上一篇文章 Jetpack Compose(一)
Compose布局
官网介绍:界面元素采用多层次结构,元素中又包含其他元素。在 Compose 中,可以通过从可组合函数中调用其他可组合函数来构建界面层次结构。
添加文本
data class Message(val author: String, val body: String)
@Composable
fun MessageCard(msg: Message) {
Column() {
Text(text = msg.author)
Text(text = msg.body)
}
}
@Preview()
@Composable
fun PreviewMessageCard(){
MessageCard(
msg = Message("Android","Hello Android")
)
}
PreviewMessageCard函数预览效果
Text:compose中的Text相当于TextView,用于显示文字。
Columm:使其中的内容垂直显示,效果与xml布局中的LinearLayout vertical类似
Row: 水平排列,与LinearLayout horizontal类似
Box:堆叠其中内容,相当于FrameLayout
添加图片
@Composable
fun MessageCard(msg: Message) {
Row(modifier = Modifier.padding(all = 4.dp)) {
Image(
painter = painterResource(R.drawable.post_4_thumb),
contentDescription = "Contact profile picture",
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
)
//在image和column中间加入空白间距
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(text = msg.author)
Text(text = msg.body)
}
}
}
预览效果
可以看到代码中使用Modifier来修改各个控件的属性,包括外观、大小、边距、位置等。官方称之为修饰符,将这些修饰符链接起来,即可创建更丰富的可组合项。修饰符列表
使用Material Design
Jetpack Compose 原生提供 Material Design 及其界面元素的实现。我们将使用 Material Design 样式改进 MessageCard 可组合项的外观。Material Design 是围绕三大要素构建的:颜色、排版、形状。
@Composable
fun MessageCard(msg: Message) {
Row(modifier = Modifier.padding(all = 4.dp)) {
Image(
painter = painterResource(R.drawable.post_4_thumb),
contentDescription = "Contact profile picture",
modifier = Modifier
//图片大小
.size(40.dp)
//裁剪为圆形
.clip(CircleShape)
//添加边框
.border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
)
//在image和column中间加入空白间距
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
//上下间距
Spacer(modifier = Modifier.height(4.dp))
//添加阴影 内边框
Surface(shape = MaterialTheme.shapes.medium,elevation = 3.dp){
Text(
text = msg.body,
style = MaterialTheme.typography.body2
)
}
}
}
}
@Preview()
@Composable
fun PreviewMessageCard() {
JetnewsTheme(){
MessageCard(
msg = Message("Android", "Hello Android")
)
}
}
//自定义 MaterialTheme 主题
@Composable
fun JetnewsTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
MaterialTheme(
colors = if (darkTheme) DarkThemeColors else LightThemeColors,
typography = JetnewsTypography,
shapes = JetnewsShapes,
content = content
)
}
给预览函数包上一个自定义的Materail风格主题JetnewsTheme,在各个控件中加入Material风格的属性,显示效果已经有了变化。
使用Material Design默认能够处理深色主题,浅色和深色主题的颜色在创建项目时就由IDE自动生成了,保存在Theme.kt文件下。预览时加入两个Preview注解即可同时显示深色和浅色主题,深色主题需加上Configuration.UI_MODE_NIGHT_YES。
动画和列表
可以使用LazyColumn
和 LazyRow来加载列表,它
只会呈现屏幕上显示的元素,因此,对于较长的列表,使用它们会非常高效 。在以下代码段中,LazyColumn
包含一个 items 子项,即列表的每一个item,会将传入的List<Message>显示出来。
@Composable
fun Conversation(messages: List<Message>) {
LazyColumn {
items(messages) { msg ->
MessageCard(msg)
}
}
}
@Preview()
@Composable
fun PreviewMessageCard() {
JetnewsTheme {
//conversationSample为List数据
Conversation(conversationSample)
}
}
此时列表预览效果
接下来实现点击消息时展开文本,为了保存item是否展开的状态,使用到 remember
和 mutableStateOf
函数。点击item改变颜色,使用 animateColorAsState
函数。
可组合函数可以使用 remember
将本地状态存储在内存中,并跟踪传递给 mutableStateOf
的值的变化。该值更新时,系统会自动重新绘制使用此状态的可组合项(及其子项)。这一功能称为重组。
简单来说,remember就是用来存储状态,而mutableStateOf用来标明这个data是有状态的,如果状态发生了改变,所有引用这个状态的控件都需要重新绘制。有点类似DataBinding,数据改变后页面也就跟着重绘。
@Composable
fun MessageCard(msg: Message) {
Row(modifier = Modifier.padding(all = 4.dp)) {
Image(
painter = painterResource(R.drawable.post_4_thumb),
contentDescription = "Contact profile picture",
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
)
//在image和column中间加入空白间距
Spacer(modifier = Modifier.width(8.dp))
//记录消息是否展开
var isExpanded by remember { mutableStateOf(false) }
//改变颜色
val surfaceColor: Color by animateColorAsState(
if (isExpanded) MaterialTheme.colors.primary
else MaterialTheme.colors.surface
)
//点击时切换isExpanded变量
Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
//上下间距
Spacer(modifier = Modifier.height(4.dp))
//添加阴影 内边框
Surface(
shape = MaterialTheme.shapes.medium,
elevation = 3.dp,
//改变颜色
color = surfaceColor,
//改变大小动画
modifier = Modifier
.animateContentSize()
.padding(1.dp)
) {
Text(
text = msg.body,
modifier = Modifier.padding(all = 4.dp),
//如果展开消息则显示所有内容,否则只显示一行
maxLines = if (isExpanded) Int.MAX_VALUE else 1,
style = MaterialTheme.typography.body2
)
}
}
}
}
总结
通过对官网教程学习,Compose的代码相比xml的方式精简得多,声明式UI的编程方式也饶有新意,学过Flutter的话还是比较容易上手的,可能突然转变写布局的方式会不太适应,但面对新技术还是要保持关注并不断尝试。