Android Jetpack Compose - a simple chat interface

foreword

At present, declarative UI has become the trend of front-end development. In addition to the initial cross-end development of React, Flutter, etc. and Web support, subsequent Android and IOS platforms have also launched declarative development. Android is developed through Jetpack Compose and Kotlin’s powerful language features. , completely get rid of the imperative use of XML files for UI layout

effect video

Android Jetpack - a simple chat interface

introduce

Before code analysis, introduce several common layouts

Row

horizontalSimilar to the arrangement of LinearLayout in Android , it is consistent with Flutter's Row.
Modifier basically performs most of the configuration work of a component. It implements a series of extension functions internally and realizes thenchain calls through functions. verticalAlignmentThe attribute is vertical alignment and horizontalArrangementhorizontal alignment

Row(
        modifier = Modifier.padding(all = 10.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Start
        )  {
         //some sub views
        }

Column

Except for the arrangement of Column and Row, the rest are roughly the same, similar to the verticalarrangement of LinearLayout

Column(
            modifier = Modifier.weight(1f),
            horizontalAlignment = Alignment.End
        ){
         //some sub views
        }

Text

Text box, similar to Android TextView, textthe attribute is the text content, colorthe attribute is the text color, stylethe attribute is the text style, you can set the text size, whether it is bold, the style, etc., the system has many built-in styles, and the textAlignattribute is the text alignment

Text(
                text = msg.author,
                color = MaterialTheme.colorScheme.secondary,
                style = MaterialTheme.typography.bodySmall,
                textAlign = TextAlign.Right
            )

Image

Equivalent to Android ImageView, painteryou can directly configure the image address (drawable) through painterResource, and modifierwe can directly modify the attributes of the image such as rounded corners, borders, and size through attributes, so there is no need to create an xml file to configure

Image(
            painter = painterResource(id = msg.img),
            contentDescription = "",
            modifier = Modifier
                .size(40.dp)
                .clickable { CircleShape }
                .border(1.dp, MaterialTheme.colorScheme.secondary, CircleShape)

        )

chat interface

By LazyColumnimplementing a vertical list, the data is fixed data, and the subscripts are located at the left and right ends of the layout, and then the default text displays one line. After clicking on the line of text, the text content will be expanded and its background color will be changed. This Demo adapted from the official

Effect

left layout

Only by adding @Composableannotations can the declarative UI be used. The declarative UI adopts a tree structure. In this example, Row is the root node of the tree and has two child nodes, namely and (the space placeholder in the middle is omitted), Imageand Columnthen ColumnIt also has two child nodes, namely Surfaceand Text, which Surfacehas another Textchild node.

Monitor whether a row is clicked, and then perform background modification mainly through the following, which is equivalent to the observer mode, monitor isExpandedthe field, and then change, it is similar to the observer immediately. The official explanation is as follows:

重组:可组合函数可以使用 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
        )

Finally, add modifier = Modifier .clickable { isExpanded = !isExpanded }it . Every click will change its value, and then get the node of the value to redraw and update

@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
                )
            }

        }
    }
}

right layout

horizontalArrangementThe right side is similar to the left side, the difference is that the arrangement is changed so that it is arranged on the right side, and then a weight attribute is added to the text modifier = Modifier.weight(1f),to prevent the line from having too much content and squeeze the right image out of the screen

@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)

        )
    }
}

insert data

Is it equivalent to Android XML RecyclerView, is it very simple, no need to write Adapterand sub-item XMl files and a lot of interfaces or something

@Composable
fun ShowMessage(msgList: List<Message>){
    //竖向列表
    LazyColumn{
        itemsIndexed(items = msgList){
            index, item ->
            if (index %2 == 0)
                MessageLeft(msg = item)
            else
                MessageRight(msg = item)
        }
    }
}

Summarize

The Jetpack Compose declarative UI may not be used at first, but after getting used to it, it will take off directly, greatly improving development efficiency and reducing development time, and most of the front-ends use declarative UI. Kung fu, such as Flutter, the two are basically similar in terms of UI, except that Flutter is divided into three parts , , and , through Widgetnesting , and judges whether the node is modified by and , so as to judge whether it needs to be redrawn. Finally, I suggest you Android developers to use it. Android Jetpack Compose has been out for almost a year, and it is gradually stabilizing.WidgetElementRenderObjectruntimetypekey

Guess you like

Origin blog.csdn.net/News53231323/article/details/128097830