Jetpack Compose 实现任务清单功能

594 阅读2分钟

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战

开始实现

首先需要一个输入框和一个按钮

Row {
    TextField(     
        modifier = Modifier.weight(2f)
    )
    Button(    
        modifier = Modifier
            .align(Alignment.CenterVertically)
            .weight(1f)
            .padding(horizontal = 8.dp)
    ) {
        Text("添加")
    }
}

c9e931be652e417e593dd5820da7343.jpg

保存输入的内容

返回一个使用传入值初始化的新MutableState用来临时保存输入的内容

var taskName by remember { mutableStateOf("") }

创建一个可观察的 MutableList 实例。

val list = remember { mutableStateListOf<String>() }

用来储存所有的任务清单

onValueChange是当输入文本时TextField触发的回调。更新的文本作为回调的参数出现

TextField(
    value = taskName,
    onValueChange = { taskName = it },
    modifier = Modifier.weight(2f)
)

接着就是显示所有任务清单了\

遍历list

for (addedName in list) {
    Card(Modifier.fillMaxWidth().padding(3.dp)) {
        Text(addedName, modifier = Modifier.fillMaxWidth().padding(12.dp))
    }
}

ezgif.com-gif-maker (12).gif

细节优化

可以看出还有细节要优化,每次输完还有清空输入框里的内容,很简单在按钮的点击事件list.add(taskName)的下面下加入

taskName = ""

删除

有添加肯定要有删除啦

在条目左边加一个删除按钮,点击删除list的数据
list.remove(addedName)

Icon(
    Icons.Filled.Delete,
    contentDescription = "Delete",
    modifier =Modifier.clickable {
        list.remove(addedName)
    }
)

ColumnScope.AnimatedVisibility

ezgif.com-gif-maker (13).gif

删除动画

可是这样没有动画呀不好看呀,
那就加呗
AnimatedVisibility当 AnimatedVisibility 位于 Column 中时,composable 会为其内容的外观和消失设置动画 出现和消失动画的进入和退出中可以定义不同的EnterTransitions和ExitTransitions。 EnterTransition 和 ExitTransition

expandVertically
这会垂直扩展出现内容的剪辑边界,从从 initialHeight 返回的高度到完整高度。 expandFrom 控制首先显示内容的哪一部分。默认情况下,剪辑边界的动画从 0 到全高,首先显示底部边缘,然后是其余内容。

shrinkVertically
这会垂直收缩消失内容的剪辑边界,从完整高度到从 targetHeight 返回的高度。 shrinkTowards 控制边界收缩动画的方向。

储存删除的内容

val deletedRouteList = remember { mutableStateListOf<String>() }

用来判断删除动画

visible = !deletedRouteList.contains(route),
enter = expandVertically(),
exit = shrinkVertically(
    animationSpec = tween(
        durationMillis = 1000,
    )
)

ezgif.com-gif-maker (14).gif

完整代码

@Composable
private fun DetailedListExample() {

    var taskName by remember { mutableStateOf("") }
    val list = remember { mutableStateListOf<String>() }
    val deletedRouteList = remember { mutableStateListOf<String>() }
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        Row {
            TextField(
                value = taskName,
                onValueChange = { taskName = it },
                modifier = Modifier.weight(2f)
            )
            Button(
                onClick = {
                    list.add(taskName)
                    taskName = ""
                },
                modifier = Modifier
                    .align(Alignment.CenterVertically)
                    .weight(1f)
                    .padding(horizontal = 8.dp)
            ) {
                Text("添加")
            }
        }
        Text(
            "TODO",
            modifier = Modifier.padding(vertical = 16.dp)
        )
        Column(

        ) {
//            for (addedName in list) {
//                Card(modifier =Modifier.fillMaxWidth().padding(3.dp),elevation = 0.dp) {
//                    Row(modifier = Modifier.fillMaxWidth().padding(12.dp)) {
//                        Text(addedName,modifier =Modifier.weight(1F))
//
//                        Icon(
//                            Icons.Filled.Delete,
//                            contentDescription = "Delete",
//                            modifier =Modifier.clickable {
//                                list.remove(addedName)
//                            }
//                        )
//                    }
//                }
//            }
            LazyColumn {
                itemsIndexed(list) { index, route ->
                    AnimatedVisibility(
                        visible = !deletedRouteList.contains(route),
                        enter = expandVertically(),
                        exit = shrinkVertically(
                            animationSpec = tween(
                                durationMillis = 1000,
                            )
                        )
                    ) {
                        Card(modifier =Modifier.fillMaxWidth().padding(3.dp),elevation = 0.dp) {
                            Row(modifier = Modifier.fillMaxWidth().padding(12.dp)) {
                                Text(route,modifier =Modifier.weight(1F))

                                Icon(
                                    Icons.Filled.Delete,
                                    contentDescription = "Delete",
                                    modifier =Modifier.clickable {
                                        deletedRouteList.add(route)
                                    }
                                )
                            }
                        }

                    }
                }
            }
        }
    }
}