Jetpack Compose——聊天界面
- 前言
- 效果视频
- 引入
- Row
- Column
- Text
- Image
- 聊天界面
- 效果
- 左边布局
- 右边布局
- 插入数据
- 总结
前言
目前声明式UI已经成为前端开发趋势,除了一开始的跨端开发React,Flutter等以及Web支持外,后续Android和IOS平台也相继推出声明式开发,Android通过Jetpack Compose配合Kotlin强大的语言特性进行开发,彻底摆脱啦命令式使用XML文件进行UI布局
效果视频
Android Jetpack ——一个简单的聊天界面
引入
在进行代码解析之前,先介绍几个常见的布局
Row
类似于Android中LinearLayout的horizontal
排列方式,与Flutter的Row一致。
Modifier基本执行了一个组件大部分配置工作,内部实现了一些列扩展函数并通过then
函数实现链式调用,verticalAlignment
属性为竖向对齐方式,horizontalArrangement
为横向对齐方式
Row(
modifier = Modifier.padding(all = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
//some sub views
}
Column
Column和Row除了排列方式不一样,其余都大致差不多,类似于LinearLayout的vertical
排列方式
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.End
){
//some sub views
}
Text
文本框,类似Android的TextView
,text
属性为文本内容,color
属性为文本颜色,style
属性为文本样式,可以设置文本大小、是否加粗、样式等,系统内置了很多样式,textAlign
属性为文本对齐方式
Text(
text = msg.author,
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Right
)
Image
等同于Android的ImageView
,painter
通过painterResource可以直接配置图片地址(drawable),通过modifier
属性我们可以直接修改图片的是否圆角、边框、大小等属性,就不需要专门去建立一个xml文件去配置
Image(
painter = painterResource(id = msg.img),
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clickable { CircleShape }
.border(1.dp, MaterialTheme.colorScheme.secondary, CircleShape)
)
聊天界面
通过LazyColumn
实现了一个竖向列表,数据为固定数据,根据下标分别位于左右俩端布局位置,然后默认文字显示一行,通过点击该行文字之后,文字内容进行展开,并改变其背景颜色,此Demo改编自官方
效果
左边布局
添加@Composable
注解,才能使用声明式UI,声明式UI采用的都是树形结构,以此例,Row为该树根节点,拥有俩个子节点,分别为Image
和Column
(中间那个空间占位符省略),然后Column
又拥有俩个子节点,分别为Surface
和Text
,其中Surface
又拥有一个Text
子节点。
监听某行是否被点击,然后执行背景修改主要通过如下,就是相当于观察者模式,监听isExpanded
字段,然后发生改变,就立即通过观察者类似。官方解释如下:
重组:可组合函数可以使用 remember 将本地状态存储在内存中,并跟踪传递给 mutableStateOf 的值的变化。
该值更新时,系统会自动重新绘制使用此状态的可组合项(及其子项)
通过使用 Compose 的状态 API(如 remember 和 mutableStateOf),系统会在状态发生任何变化时自动更新界面。
//监听isExpanded字段
var isExpanded by remember { mutableStateOf(false) }
//收缩和扩展对应俩种颜色,由isExpanded字段决定
val surfaceColor by animateColorAsState(
targetValue = if (isExpanded)
Color.Cyan
else
MaterialTheme.colorScheme.surface
)
最后在需要添加点击方法的组件中添加 modifier = Modifier .clickable { isExpanded = !isExpanded }
即可,每一次点击都改变其值,然后获取该值的节点进行重绘,更新
@Composable
fun MessageLeft(msg : Message){
Row(
modifier = Modifier.padding(all = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start) {
//图像
Image(
painter = painterResource(id = msg.img),
contentDescription = "",
modifier = Modifier
.size(40.dp)//图像大小
.clip(CircleShape)
.border(1.dp, MaterialTheme.colorScheme.secondary, CircleShape)
)
//左右间隔
Spacer(modifier = Modifier.width(10.dp))
//重组:可组合函数可以使用 remember 将本地状态存储在内存中,并跟踪传递给 mutableStateOf 的值的变化。
// 该值更新时,系统会自动重新绘制使用此状态的可组合项(及其子项)
//通过使用 Compose 的状态 API(如 remember 和 mutableStateOf),系统会在状态发生任何变化时自动更新界面。
var isExpanded by remember { mutableStateOf(false) }
val surfaceColor by animateColorAsState(
targetValue = if (isExpanded)
Color.Cyan
else
MaterialTheme.colorScheme.surface
)
Column() {
Text(
text = msg.author,
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodySmall
)
//上下间隔
Spacer(modifier = Modifier.height(10.dp))
Surface(
shape = RectangleShape,
shadowElevation = 1.dp,
tonalElevation = 1.dp,
color = surfaceColor,
modifier = Modifier
.animateContentSize()
.padding(1.dp)
) {
Text(
text = msg.content,
modifier = Modifier
.clickable { isExpanded = !isExpanded }
.padding(all = 4.dp),
maxLines = if (isExpanded) Int.MAX_VALUE else 1,
style = MaterialTheme.typography.bodyMedium,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
右边布局
右边与左边差不多,不同点在于改变了horizontalArrangement
排列方式,使其呈右边排列,然后给文字添加了一个权重属性 modifier = Modifier.weight(1f),
,防止该行内容过多,将右边图像挤到屏幕之外
@Composable
fun MessageRight(msg: Message){
Row(
modifier = Modifier
.padding(10.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End) {
//给左边文字一个权重,避免文字过多,让右边图像无法显示
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.End
) {
Text(
text = msg.author,
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Right
)
Spacer(modifier = Modifier.height(10.dp))
var isExpanded by remember { mutableStateOf(false) }
val surfaceColor by animateColorAsState(
targetValue = if (isExpanded)
Color.Green
else
MaterialTheme.colorScheme.surface
)
Surface(
shape = RectangleShape,
shadowElevation = 1.dp,
tonalElevation = 1.dp,
color = surfaceColor,
modifier = Modifier
.animateContentSize()
.padding(1.dp)
) {
Text(
text = msg.content,
modifier = Modifier
.clickable { isExpanded = !isExpanded }
.padding(all = 4.dp),
maxLines = if (isExpanded) Int.MAX_VALUE else 1,
style = MaterialTheme.typography.bodyMedium,
overflow = TextOverflow.Ellipsis
)
}
}
Spacer(modifier = Modifier.width(10.dp))
Image(
painter = painterResource(id = msg.img),
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clickable { CircleShape }
.border(1.dp, MaterialTheme.colorScheme.secondary, CircleShape)
)
}
}
插入数据
等同于Android XML的RecyclerView
,是不是很简单,不需要写Adapter
和子项XMl文件以及一大堆接口什么的啦
@Composable
fun ShowMessage(msgList: List<Message>){
//竖向列表
LazyColumn{
itemsIndexed(items = msgList){
index, item ->
if (index %2 == 0)
MessageLeft(msg = item)
else
MessageRight(msg = item)
}
}
}
总结
Jetpack Compose 声明式UI一开始用起来可能不太习惯,但是适应之后,直接起飞,大大提高开发效率,减少开发时间,而且大部分前端都使用声明式UI,想要兼修,就不必费很大功夫,就例如Flutter,从UI而言俩个基本都差不多,只不过Flutter通过Widget
进行嵌套,分为Widget
、Element
、RenderObject
三个部分,通过runtimetype
和key
判断此节点是否修改,从而判断是否需要进行重绘。
最后建议诸位Android开发者用起来,Android Jetpack Compose已经差不多出来有一年时间啦,逐渐稳定