安卓待办清单APP完整源代码
package com.example.bringlistimport android.content.Contextimport androidx.compose.foundation.Image;import android.os.Bundleimport android.widget.Toastimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.compose.foundation.ExperimentalFoundationApiimport androidx.compose.foundation.backgroundimport androidx.compose.foundation.layout.*import androidx.compose.foundation.lazy.LazyColumnimport androidx.compose.foundation.lazy.itemsimport androidx.compose.foundation.shape.RoundedCornerShapeimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Deleteimport androidx.compose.material.icons.filled.Doneimport androidx.compose.material.icons.filled.Infoimport androidx.compose.material.icons.filled.Personimport androidx.compose.material3.*import androidx.compose.runtime.*import androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.res.painterResourceimport androidx.compose.ui.text.style.TextDecorationimport androidx.compose.ui.unit.dpimport androidx.lifecycle.lifecycleScopeimport java.util.Collections.listimport androidx.room.*import com.example.bringlist.ui.theme.BringlistThemeimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.launchimport kotlinx.coroutines.withContextimport android.content.ClipDataimport android.content.ClipboardManagerimport androidx.compose.foundation.combinedClickableimport java.text.SimpleDateFormat;import java.util.Date;import java.util.Locale;// ====================== 1. 数据库实体(本地存储用)======================@Entity(tableName = "todo_table")data class Todo1(@PrimaryKey val id: Int ,val text: String,val isDone: Boolean = false)// ====================== 2. Dao(增删改查)======================@Daointerface TodoDao {@Query("SELECT * FROM todo_table")funloadAllUsers():List<Todo1>@Query("delete FROM todo_table")suspend fundeleteAllUsers()@Insertsuspend funinsert(todo: Todo1)@Query("update todo_table set text=:newtext where text=:oldtext")suspend funupdatetext(newtext:String,oldtext:String)@Query("delete from todo_table where text=:texttemp")suspend fundeletetodobytext(texttemp: String)@Query("update todo_table set id=:newid where id=:tempid")suspend funupdateid(newid:Int,tempid:Int)@Query("update todo_table set isDone=:newisDone where text=:temptext")suspend funupdatedata(newisDone: Boolean,temptext: String)}// ====================== 3. 数据库 ======================@Database(entities = [Todo1::class], version = 1, exportSchema = false)abstract class TodoDatabase : RoomDatabase() {abstract funtodoDao(): TodoDaocompanion object {@Volatileprivate var INSTANCE: TodoDatabase? = nullfungetInstance(context: android.content.Context): TodoDatabase {return INSTANCE ?: synchronized(this) {val instance = Room.databaseBuilder(context.applicationContext,TodoDatabase::class.java,"todo_database").build()INSTANCE = instanceinstance}}}}// 工具函数:显示Toast提示funshowToast(context: Context, message: String) {Toast.makeText(context, message, Toast.LENGTH_LONG).show()}// 数据类:每个待办事项data class Todo(var id: Int,val text: String,val isDone: Boolean = false)class MainActivity : ComponentActivity() {private val todoList = mutableStateListOf<Todo>()private lateinit var todoDao:TodoDao// 定义一个变量存储获取todoList的回调函数// private var getTodoListCallback: (() -> List<Todo>)? = nulloverride funonCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)init()setContent {// 主题BringlistTheme {Surface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {TodoApp( todoList = todoList // initialTodos = initialTodos,) // 主界面}}}}funinit(){todoDao=TodoDatabase.getInstance(this).todoDao()lifecycleScope.launch(Dispatchers.IO) {val savedTodos=todoDao.loadAllUsers()withContext(Dispatchers.Main){todoList.clear()todoList.addAll(savedTodos.map {todo1 ->Todo(id=todo1.id,text=todo1.text,isDone = todo1.isDone)})}}}override funonResume() {super.onResume()init()}}// 主界面@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)@ComposablefunTodoApp( todoList:MutableList<Todo>// initialTodos: List<Todo>,) {// 获取上下文(用于Toast提示)val context = LocalContext.currentval todoDao = TodoDatabase.getInstance(context).todoDao()// 输入框内容var newTodoText by remember { mutableStateOf("") }// ========== 对话框相关状态 ==========var showUnCarriedDialog by remember { mutableStateOf(false) } // 未携带对话框显示状态var showCarriedDialog by remember { mutableStateOf(false) } // 已携带对话框显示状态var showPersonInfoDialog by remember { mutableStateOf(false) } // 个人信息对话框// 存储要显示的提示内容var dialogTitle by remember { mutableStateOf("") }var dialogContent by remember { mutableStateOf("") }// 编辑相关状态var showEditDialog by remember { mutableStateOf(false) }var currentEditTodo by remember { mutableStateOf<Todo?>(null) }var editText by remember { mutableStateOf("") }val coroutineScope = rememberCoroutineScope()// 构建未携带物件的提示内容funbuildUnCarriedContent() {val unCarriedItems = todoList.filter { !it.isDone }dialogTitle = "未完成事项"//携带物品"dialogContent = if (unCarriedItems.isEmpty()) {"所有事项都完成"//物件都已携带"} else {unCarriedItems.joinToString(separator = "\n") { "• ${it.text}" }}showUnCarriedDialog = true}// 构建已携带物件的提示内容funbuildCarriedContent() {val carriedItems = todoList.filter { it.isDone }dialogTitle = "已完成事项"//已携带物品"dialogContent = if (carriedItems.isEmpty()) {"暂无已完成事项"//携带物品"} else {carriedItems.joinToString(separator = "\n") { "• ${it.text}" }}showCarriedDialog = true}Box(modifier = Modifier.fillMaxSize().padding(16.dp)) {Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {// 顶部:输入框 + 添加按钮Row(modifier = Modifier.fillMaxWidth(),verticalAlignment = Alignment.CenterVertically) {TextField(value = newTodoText,onValueChange = { newTodoText = it },modifier = Modifier.weight(1f),placeholder = { Text("请输入待办事项") },//需携带物品singleLine = true)Spacer(modifier = Modifier.width(5.dp))Button(onClick = {if (newTodoText.isNotBlank()) {todoList.add(Todo(id = todoList.size + 1,text = newTodoText))newTodoText = "" // 清空输入框coroutineScope.launch {var i=todoList.size-1todoDao.insert (Todo1(todoList[i].id, todoList[i].text, todoList[i].isDone))}}}) {Text("添加")}Spacer(modifier = Modifier.width(5.dp))Button(onClick = {val clipboardManager =context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager// val Items=todoList.listIterator()val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())var text = sdf.format(Date())+"\n"//var text=""todoList.forEach { item ->text += if (item.isDone) item.text+"(已完成)\n" else item.text+"(未完成)\n"}//var text=todoList.joinToString(separator = "\n") { " ${it.text}" }val clipData = ClipData.newPlainText("text_label", text)clipboardManager.setPrimaryClip(clipData)showToast(context, "复制成功")}//colors = ButtonDefaults.buttonColors(containerColor = Color.Blue, contentColor = Color.White)) {Text("导出")}}Spacer(modifier = Modifier.height(16.dp))// 列表LazyColumn(modifier = Modifier.fillMaxWidth()) {items(todoList, key = { it.id }) { todo ->TodoItem(todo = todo,onToggle = { isDone ->todoList[todoList.indexOfFirst { it.id == todo.id }] = todo.copy(isDone = isDone)//状态传进去,并拷贝coroutineScope.launch {todoDao.updatedata(isDone,todo.text)}},onDelete = {todoList.remove(todo)for(i in todo.id..todoList.size){todoList[i-1].id=i}//写数据库更改idcoroutineScope.launch {todoDao.deletetodobytext(todo.text)for(i in todo.id..todoList.size){todoDao.updateid(i,i+1)}}},// 长按触发编辑onLongClick = {currentEditTodo = todoeditText = todo.textshowEditDialog = true})}}}// ========== 底部悬浮按钮(重构为垂直布局) ==========Column(modifier = Modifier.align(Alignment.BottomEnd).padding(bottom = 40.dp, end = 16.dp),verticalArrangement = Arrangement.spacedBy(12.dp) // 按钮垂直间距) {// 第三个悬浮按钮:个人信息(位于最上方)FloatingActionButton(onClick = { showPersonInfoDialog = true }, // 激活个人信息对话框containerColor = MaterialTheme.colorScheme.primary, // 第三色,区分前两个按钮modifier = Modifier.size(56.dp).offset(x = 68.dp, y = 0.dp) // 向右偏移 8dp,贴近右边缘) {Icon(Icons.Default.Person, contentDescription = "个人信息", tint = Color.White)}// ========== 关键修改2:底部悬浮按钮区域 ==========Row(horizontalArrangement = Arrangement.spacedBy(12.dp) // 水平间距) {// 悬浮按钮1:提示所有未携带物件FloatingActionButton(onClick = {buildUnCarriedContent()},containerColor = MaterialTheme.colorScheme.primary, // 红色强调未完成modifier = Modifier.size(56.dp)) {Icon(Icons.Default.Info, contentDescription = "未携带物品", tint = Color.White)}// 悬浮按钮2:提示所有已携带物件FloatingActionButton(onClick = {buildCarriedContent()},containerColor = MaterialTheme.colorScheme.primary, // 主题主色modifier = Modifier.size(56.dp)) {Icon(Icons.Default.Done, contentDescription = "已携带物品", tint = Color.White)}}}}// ========== 未携带物件对话框 ==========if (showUnCarriedDialog) {AlertDialog(onDismissRequest = { showUnCarriedDialog = false }, // 点击外部关闭title = { Text(dialogTitle) },text = { Text(dialogContent) },confirmButton = {TextButton(onClick = { showUnCarriedDialog = false }) {Text("确认")}})}// ========== 已携带物件对话框 ==========if (showCarriedDialog) {AlertDialog(onDismissRequest = { showCarriedDialog = false },title = { Text(dialogTitle) },text = { Text(dialogContent) },confirmButton = {TextButton(onClick = { showCarriedDialog = false }) {Text("确认")}})}// ========== 新增:个人信息对话框(含二维码) ==========if (showPersonInfoDialog) {AlertDialog(onDismissRequest = { showPersonInfoDialog = false },title = { Text("赞赏", style = MaterialTheme.typography.titleLarge) },// 自定义对话框内容:二维码 + 个人信息text = {Column(horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.spacedBy(16.dp),modifier = Modifier.padding(8.dp)) {// 二维码图片(支持本地/网络图片)Image(painter = painterResource(id = R.drawable.shoukuanma), // qrcode 是你放在 drawable 里的二维码图片名contentDescription = "收款码",modifier = Modifier.size(250.dp).background(Color.White, RoundedCornerShape(8.dp)).padding(8.dp),alignment = Alignment.Center)// 个人信息文本Column(horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.spacedBy(8.dp)) {Text("可截图二维码...”", style = MaterialTheme.typography.bodyMedium)Text("识别二维码赞助...", style = MaterialTheme.typography.bodyMedium)Text("可关注公众号“路边米花”赞赏", style = MaterialTheme.typography.bodyMedium)//, color = Color.Gray)}}},confirmButton = {TextButton(onClick = { showPersonInfoDialog = false }) {Text("关闭")}},// 调整对话框形状和内边距shape = RoundedCornerShape(12.dp),modifier = Modifier.padding(16.dp))}//}// 编辑对话框if (showEditDialog && currentEditTodo != null) {val oldtext= currentEditTodo!!.textAlertDialog(onDismissRequest = { showEditDialog = false },title = { Text("编辑待办") },text = {TextField(value = editText,onValueChange = { editText = it },singleLine = true,modifier = Modifier.fillMaxWidth())},confirmButton = {Button(onClick = {currentEditTodo?.let { todo ->val index = todoList.indexOf(todo)if (index != -1 && editText.isNotBlank()) {todoList[index] = todo.copy(text = editText)}}showEditDialog = falsecurrentEditTodo = nullcoroutineScope.launch {todoDao.updatetext(editText, oldtext)}}) {Text("保存")}},dismissButton = {Button(onClick = {showEditDialog = falsecurrentEditTodo = null}) {Text("取消")}})}}// 单个待办条目@OptIn(ExperimentalFoundationApi::class)@ComposablefunTodoItem(todo: Todo,onToggle: (Boolean) -> Unit,onDelete: () -> Unit,onLongClick: () -> Unit) {Row(modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.surface).combinedClickable(onClick = {},onLongClick = onLongClick // 长按).padding(12.dp),verticalAlignment = Alignment.CenterVertically) {// 勾选框Checkbox(checked = todo.isDone,onCheckedChange = onToggle)Text(text = if(todo.isDone) todo.text+"(已完成)" else todo.text,modifier = Modifier.weight(1f),// 完成就加删除线textDecoration = if (todo.isDone) TextDecoration.LineThrough else TextDecoration.None,color = if (todo.isDone) Color.Gray else LocalContentColor.current)// 删除按钮IconButton(onClick = onDelete) {Icon(Icons.Default.Delete, contentDescription = "删除")}}}
夜雨聆风