在完成了基础之后,现在我们有了一个近乎完整的软件框架了,现在就一步步实现对应功能就好了。
本篇要实现的就是:
功能1:首页中的修改小说名称
功能2:删除小说。
但是交互略显低端,之后有需要的话再优化。
说一下实现逻辑:
功能1:首先点击主页中小说条目左侧的笔的按钮,跳转到新建小说页面,对小说名字进行修改
功能2:点击主页中小说条目右侧的垃圾桶的按钮,弹出提示框,确认后删除
为了对特定名字进行修改,首先我们要获取小说的id和名字,并将他们传递到修改名字页面去。
因此要现在PageNavHost中定义变量:
composable(
"fic_name_page?fictionId={fictionId}?fictionName={fictionName}",
arguments = listOf(
navArgument("fictionId") {
defaultValue = 0
type = NavType.IntType} ,
navArgument("fictionName") {
defaultValue = ""
type = NavType.StringType}
)
) { backStackEntry ->
//backStackEntry里面封装了参数
FictionNamePage(navController, backStackEntry)
}
这其中和之前的有些许不同,主要体现在路线route上
当我们传递无默认值参数时可以这样写
"fic_name_page/{fictionId}/{fictionName}"
但是当我们想传递有默认值参数时,则需要写成上面那种形式,同时在下面的代码中添加defaultValue值,注意,这里最好指定参数的数据类型,不然他会默认都按照String类型传递。
之后,我们只需要给按钮添加带参数的路线即可:
navController.navigate("fic_name_page?fictionId=${it.fictionId}?fictionName=${it.fictionName}")
注意这里的东西一点都不能少,少了就报错。
这也会影响原来主页面右上角加号按钮的跳转,修改代码为:
navController.navigate("fic_name_page"){ popUpTo("main_page") }
这里加了个popUpTo,这个是指定返回时跳转到什么页面,具体好不好用现在还看不出来。
数据发送就算完成了,接受这边就更简单了,转到FictionNamePage,只需要将值取出即可。
var fictionNameText by remember { mutableStateOf("") }
val fictionName = backStackEntry.arguments?.getString("fictionName")
val fictionId = backStackEntry.arguments?.getInt("fictionId")
fictionNameText = fictionName!!
这里额外定义了fictionNameText 全局值,这个变量用来存放输入框的值,所以不能去掉。
接下来就是按钮里判断一下的事了:
IconButton(onClick = {
if (fictionId == 0) {
viewModel.insertBook(fictionNameText)
Toast.makeText(context, "创建成功", Toast.LENGTH_SHORT).show()
navController.popBackStack("main_page", false)
} else {
viewModel.updateBook(fictionId!!, fictionNameText)
Toast.makeText(context, "修改成功", Toast.LENGTH_SHORT).show()
navController.popBackStack("main_page", false)
}
}, modifier = Modifier.size(50.dp)) {
Image(
painter = painterResource(id = R.drawable.btn_finish),
contentDescription = "add a book"
)
}
双感叹号就是强制确定它不为空,当然这是在确定的情况下,随便乱用会报错。
这个功能的实现有一个关键,就是弹提示框:AlertDialog。
提示框可以稍微自定义一下,但是我抄的基本和原来的没啥区别:
@Composable
fun CustomAlertDialog(
title:String,
message:String,
onConfirm:() -> Unit,
onDismiss:() -> Unit
) {
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(text = title)
},
text = { Text(text = message) },
confirmButton = {
TextButton(onClick = onConfirm) {
Text(text = "确认")
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(text = "取消")
}
}
)
}
那么有了自定义的AlertDialog之后,我们就要应用到页面中了。
这里有个小注意的点,AlertDialog只能在有布局的页面中,所以要放到MainPage的最外层与Scaffold并列,这样就能全屏显示提示框了。
首先定义变量:
var openDialogFlag by remember { mutableStateOf(false) }
var deteleId by remember { mutableStateOf(0) }
一个用于控制对话框的显示,一个用于传递参数,这个后面就能看到。
然后就可以写提示框代码了:
if (openDialogFlag) {
CustomAlertDialog(
title = "警告",
message = "确认要删除吗?这将删除本书所有信息!",
onConfirm = {
openDialogFlag = false
viewModel.deleteBookAndAll(deteleId)
deteleId = 0
},
onDismiss = {
openDialogFlag = false
}
)
}
可以看到deteleId用于这里传递参数。
之后就是按钮的操作代码:
IconButton(onClick = {
deteleId = it.fictionId
openDialogFlag = true
},}
其实重点就是onclick里面的两行。deteleId的实际作用就是将it中的参数传递出来。
这样代码就完成了,一起看看效果: