• Android Jetpack Compose——一个简单的聊天界面


    前言

    目前声明式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
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Column

    Column和Row除了排列方式不一样,其余都大致差不多,类似于LinearLayout的vertical排列方式

    Column(
                modifier = Modifier.weight(1f),
                horizontalAlignment = Alignment.End
            ){
             //some sub views
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Text

    文本框,类似Android的TextViewtext属性为文本内容,color属性为文本颜色,style属性为文本样式,可以设置文本大小、是否加粗、样式等,系统内置了很多样式,textAlign属性为文本对齐方式

    Text(
                    text = msg.author,
                    color = MaterialTheme.colorScheme.secondary,
                    style = MaterialTheme.typography.bodySmall,
                    textAlign = TextAlign.Right
                )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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)
    
            )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    聊天界面

    通过LazyColumn实现了一个竖向列表,数据为固定数据,根据下标分别位于左右俩端布局位置,然后默认文字显示一行,通过点击该行文字之后,文字内容进行展开,并改变其背景颜色,此Demo改编自官方

    效果

    左边布局

    添加@Composable注解,才能使用声明式UI,声明式UI采用的都是树形结构,以此例,Row为该树根节点,拥有俩个子节点,分别为ImageColumn(中间那个空间占位符省略),然后Column又拥有俩个子节点,分别为SurfaceText,其中Surface又拥有一个Text子节点。

    监听某行是否被点击,然后执行背景修改主要通过如下,就是相当于观察者模式,监听isExpanded字段,然后发生改变,就立即通过观察者类似。官方解释如下:

    重组:可组合函数可以使用 remember 将本地状态存储在内存中,并跟踪传递给 mutableStateOf 的值的变化。
    该值更新时,系统会自动重新绘制使用此状态的可组合项(及其子项)
    通过使用 Compose 的状态 API(如 remember 和 mutableStateOf),系统会在状态发生任何变化时自动更新界面。
    
    • 1
    • 2
    • 3
    //监听isExpanded字段
    var isExpanded by remember { mutableStateOf(false) }
    
    //收缩和扩展对应俩种颜色,由isExpanded字段决定
            val surfaceColor by animateColorAsState(
                targetValue = if (isExpanded)
                    Color.Cyan
                else
                    MaterialTheme.colorScheme.surface
            )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    最后在需要添加点击方法的组件中添加 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
                    )
                }
    
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    右边布局

    右边与左边差不多,不同点在于改变了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)
    
            )
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    插入数据

    等同于Android XML的RecyclerView,是不是很简单,不需要写Adapter和子项XMl文件以及一大堆接口什么的啦

    @Composable
    fun ShowMessage(msgList: List){
        //竖向列表
        LazyColumn{
            itemsIndexed(items = msgList){
                index, item ->
                if (index %2 == 0)
                    MessageLeft(msg = item)
                else
                    MessageRight(msg = item)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    总结

    Jetpack Compose 声明式UI一开始用起来可能不太习惯,但是适应之后,直接起飞,大大提高开发效率,减少开发时间,而且大部分前端都使用声明式UI,想要兼修,就不必费很大功夫,就例如Flutter,从UI而言俩个基本都差不多,只不过Flutter通过Widget进行嵌套,分为WidgetElementRenderObject三个部分,通过runtimetypekey判断此节点是否修改,从而判断是否需要进行重绘。
    最后建议诸位Android开发者用起来,Android Jetpack Compose已经差不多出来有一年时间啦,逐渐稳定

  • 相关阅读:
    Pinia基础知识
    Virtualbox ArchLinux 安装
    Base64编码相关知识总结
    网络安全协议—SSL
    表示学习(Representation learning)以及相关(半监督)论文阅读
    图08 --- 网络的最大流
    MySQL---JDBC编程
    QT之QPropertyAnimation动画类的介绍
    审稿意见-知识图谱创新性问题请教
    打包工具webpack的学习
  • 原文地址:https://blog.csdn.net/News53231323/article/details/128097830