Android Compose 04 列表与状态

前言

  在上一节中了解了基本的布局组件,并在最后制作了一个消息卡片。这一节我们接着消息卡片,制作一个可以滑动的消息列表。

变量提取

  首先要将消息卡片中的变量提取出来,新建一个用于存储消息卡片的实体类,变量包括消息内容以及用户名(用户头像也可以作为变量,这里为了方便采用统一头像),代码如下

data class MessageData(
    val message: String,
    val username: String,
)

  将上一节代码中的变量进行相应的替换并将其装入一个可组合函数中,传入参数为MessageData,代码如下(这里将文字的颜色改为了黑色,并调整了控件大小,更加贴合实机上的演示效果)

@Composable
fun MessageCard(message: MessageData) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        modifier = Modifier
            .padding(horizontal = 10.dp, vertical = 20.dp)
    ) {
        Image(
            painter = painterResource(id = R.drawable.melon),
            contentDescription = "icon",
            modifier = Modifier
                .size(80.dp)
                .clip(CircleShape)
        )
        Spacer(modifier = Modifier.width(10.dp))
        Column {
            Text(
                text = message.username,
                fontSize = 20.sp,
                color = Color.White
            )
            Spacer(modifier = Modifier.height(10.dp))
            Text(
                text = message.message,
                fontSize = 18.sp,
                color = Color.White
            )
        }
    }
}

可滑动列表

  在view系统中,我们要实现一个可滑动的列表我们需要配置使用RecyclerView。用过的大家应该都清楚RecyclerView需要配置麻烦的adapter以及layoutManager,在Compose中我们可以使用LazyColumn/LazyRow轻松实现。

@Composable
fun MessageList(list: List<MessageData>) {
    LazyColumn {
        items(list) {
            MessageCard(message = it)
        }
    }
}

  LazyColomn中的block不是Composable函数,要使用Compose就必须使用 item/items block,这里的作用相当于View系统中RecyclerView的item。items相当于一个循环可以向参数中传入 List或Array 并在block中调用 it 获取到参数中的每一个值,或者也可以传入一个数字代表将其中的 item 重复多少次。

  我们只需要向MessageList导入对应的包含message对象的list即可实现可以滑动的List了,效果如下:

  但是这里需要值得注意的是,只有在 LazyList/LazyColumn 中的范围才能支持滑动,所以我们在实现滑动操作时最好将 LazyList/LazyColumn 铺满屏幕,除非有一些其他的需求。

可滑动范围示意图

状态

  状态(State)作为Compose中至关重要的一环,它决定着界面保存的信息以及刷新的逻辑也是声明式UI中不可或缺的一环。在本节仅是简单的介绍一下,后面会有详细讲解的章节。

  在我们原来的基础上再添加一个要求,消息卡片中展示的消息可以是很长的消息,但是仅展示一行。当用户点击消息时再展开全部的消息,再次点击时收回消息。这也是现代很多App中采用的设计。

  在AnroidView系统中可能需要对消息的长度进行测量,随后再去根据测量结果绘制相应的View,这种实现方式较为麻烦。在Compose中我们可以简单的控制 TextMaxLines 属性去实现它。代码以及效果如下:

展开收起信息效果
@Composable
fun MessageCard(message: MessageData) {
    var isExpanded by remember{ mutableStateOf(false) }
    Row(
        verticalAlignment = Alignment.CenterVertically,
        modifier = Modifier
            .padding(horizontal = 10.dp, vertical = 20.dp)
            .fillMaxWidth()
            .clickable { isExpanded = !isExpanded }
    ) {
        Image(
            painter = painterResource(id = R.drawable.melon),
            contentDescription = "icon",
            modifier = Modifier
                .size(80.dp)
                .clip(CircleShape)
        )
        Spacer(modifier = Modifier.width(10.dp))
        Column {
            Text(
                text = message.username,
                fontSize = 20.sp,
                color = Color.Black
            )
            Spacer(modifier = Modifier.height(10.dp))
            Text(
                text = message.message,
                fontSize = 18.sp,
                color = Color.Black,
                maxLines = if (isExpanded) Int.MAX_VALUE else 1
            )
        }
    }
}

  我们在Composable函数中添加了一个 isExpanded 变量以及在 Row 中添加了一个clickable方法让其可以监听点击时间,最后在显示message的 Text 中传入了maxLines参数控制其能显示的最大行数。

  其中 maxLines = if (isExpanded) Int.MAX_VALUE else 1 比较好理解,判断如果是展开状态就有多少行,否则就只显示一行。但是这里值得注意的是我们定义变量不能像常规写代码时一样,直接 var isExpanded = false 这样定义变量在Compose进行重组,也就是刷新UI布局的时候变量就会丢失,所以我们要使用remember来定义Compose中会影响到数据显示以及布局展示的变量。

  remember的作用相当于开辟了一块独立的空间用于存放Composable函数中的变量,在其完成重组后仍能保持修改后的状态。remember block中要使用与State相关的对象,并且在赋值是可以采用by执行委托方式赋值,直接使用 = 赋值会获取到 MutableState 对象,而采用委托方式赋值可以获取到对象中保存的值(在这个示例代码中是false)


初めて会ったの日から 僕の心の全てを奪った