diff --git a/README.md b/README.md index f351ee1..3ee7292 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,18 @@ - [x] 成员消费分析 - [x] 自定义统计周期 +### 2.1 图标美化计划 (进行中 🎨) +- [ ] 食品类图标 (餐饮、零食、饮料等) +- [ ] 交通类图标 (公交、打车、加油等) +- [ ] 购物类图标 (超市、数码、服装等) +- [ ] 居住类图标 (房租、水电、物业等) +- [ ] 医疗类图标 (药品、诊疗、保健等) +- [ ] 娱乐类图标 (游戏、电影、旅游等) +- [ ] 学习类图标 (书籍、课程、文具等) +- [ ] 其他类图标 (礼物、捐赠、其他等) +- [ ] 收入类图标 (工资、奖金、理财等) +- [ ] 成员图标 (家人、朋友、同事等) + ### 3. 数据管理 (进行中 🚀) - [ ] 导出 CSV/Excel 功能 - [ ] 数据迁移工具 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f232bbc..9b4b951 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,8 +16,8 @@ android { applicationId = "com.yovinchen.bookkeeping" minSdk = 26 targetSdk = 34 - versionCode = 5 - versionName = "1.2.4" + versionCode = 6 + versionName = "1.3.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/com/yovinchen/bookkeeping/data/BookkeepingDatabase.kt b/app/src/main/java/com/yovinchen/bookkeeping/data/BookkeepingDatabase.kt index 397b009..c1e43b6 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/data/BookkeepingDatabase.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/data/BookkeepingDatabase.kt @@ -8,6 +8,7 @@ import androidx.room.RoomDatabase import androidx.room.TypeConverters import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase +import com.yovinchen.bookkeeping.R import com.yovinchen.bookkeeping.model.BookkeepingRecord import com.yovinchen.bookkeeping.model.Category import com.yovinchen.bookkeeping.model.Converters @@ -19,7 +20,7 @@ import kotlinx.coroutines.launch @Database( entities = [BookkeepingRecord::class, Category::class, Member::class], - version = 3, + version = 4, exportSchema = false ) @TypeConverters(Converters::class) @@ -38,14 +39,15 @@ abstract class BookkeepingDatabase : RoomDatabase() { CREATE TABLE IF NOT EXISTS members ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, - description TEXT NOT NULL DEFAULT '' + description TEXT NOT NULL DEFAULT '', + icon INTEGER NOT NULL DEFAULT 0 ) """) // 插入默认成员 db.execSQL(""" - INSERT INTO members (name, description) - VALUES ('自己', '默认成员') + INSERT INTO members (name, description, icon) + VALUES ('自己', '默认成员', ${R.drawable.ic_member_boy_24dp}) """) // 修改记账记录表,添加成员ID字段 @@ -96,14 +98,15 @@ abstract class BookkeepingDatabase : RoomDatabase() { CREATE TABLE IF NOT EXISTS categories_new ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, - type TEXT NOT NULL + type TEXT NOT NULL, + icon INTEGER NOT NULL DEFAULT 0 ) """) // 复制分类数据 db.execSQL(""" - INSERT INTO categories_new (id, name, type) - SELECT id, name, type FROM categories + INSERT INTO categories_new (id, name, type, icon) + SELECT id, name, type, 0 FROM categories """) // 删除旧表 @@ -114,6 +117,13 @@ abstract class BookkeepingDatabase : RoomDatabase() { } } + private val MIGRATION_3_4 = object : Migration(3, 4) { + override fun migrate(db: SupportSQLiteDatabase) { + // 如果需要,在这里添加数据库迁移逻辑 + // 由于这次更改可能只是schema hash的变化,我们不需要实际的数据库更改 + } + } + @Volatile private var INSTANCE: BookkeepingDatabase? = null @@ -124,7 +134,7 @@ abstract class BookkeepingDatabase : RoomDatabase() { BookkeepingDatabase::class.java, "bookkeeping_database" ) - .addMigrations(MIGRATION_1_2, MIGRATION_2_3) + .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) .addCallback(object : Callback() { override fun onCreate(db: SupportSQLiteDatabase) { super.onCreate(db) @@ -136,40 +146,52 @@ abstract class BookkeepingDatabase : RoomDatabase() { // 初始化默认成员 database.memberDao().apply { if (getMemberCount() == 0) { - insertMember(Member(name = "自己", description = "默认成员")) - insertMember(Member(name = "老婆", description = "默认成员")) - insertMember(Member(name = "老公", description = "默认成员")) - insertMember(Member(name = "家庭", description = "默认成员")) - insertMember(Member(name = "儿子", description = "默认成员")) - insertMember(Member(name = "女儿", description = "默认成员")) - insertMember(Member(name = "爸爸", description = "默认成员")) - insertMember(Member(name = "妈妈", description = "默认成员")) - insertMember(Member(name = "爷爷", description = "默认成员")) - insertMember(Member(name = "奶奶", description = "默认成员")) - insertMember(Member(name = "外公", description = "默认成员")) - insertMember(Member(name = "外婆", description = "默认成员")) - insertMember(Member(name = "其他人", description = "默认成员")) + insertMember(Member(name = "自己", description = "默认成员", icon = R.drawable.ic_member_boy_24dp)) + insertMember(Member(name = "老婆", description = "默认成员", icon = R.drawable.ic_member_girl_24dp)) + insertMember(Member(name = "老公", description = "默认成员", icon = R.drawable.ic_member_boy_24dp)) + insertMember(Member(name = "家庭", description = "默认成员", icon = R.drawable.ic_member_family_24dp)) + insertMember(Member(name = "儿子", description = "默认成员", icon = R.drawable.ic_member_baby_boy_24dp)) + insertMember(Member(name = "女儿", description = "默认成员", icon = R.drawable.ic_member_baby_girl_24dp)) + insertMember(Member(name = "爸爸", description = "默认成员", icon = R.drawable.ic_member_father_24dp)) + insertMember(Member(name = "妈妈", description = "默认成员", icon = R.drawable.ic_member_mother_24dp)) + insertMember(Member(name = "爷爷", description = "默认成员", icon = R.drawable.ic_member_grandfather_24dp)) + insertMember(Member(name = "奶奶", description = "默认成员", icon = R.drawable.ic_member_grandmother_24dp)) + insertMember(Member(name = "外公", description = "默认成员", icon = R.drawable.ic_member_grandfather_24dp)) + insertMember(Member(name = "外婆", description = "默认成员", icon = R.drawable.ic_member_grandmother_24dp)) + insertMember(Member(name = "其他人", description = "默认成员", icon = R.drawable.ic_member_boy_24dp)) } } - // 初始化默认分类 +// 初始化默认分类 database.categoryDao().apply { // 支出分类 - insertCategory(Category(name = "餐饮", type = TransactionType.EXPENSE)) - insertCategory(Category(name = "交通", type = TransactionType.EXPENSE)) - insertCategory(Category(name = "购物", type = TransactionType.EXPENSE)) - insertCategory(Category(name = "娱乐", type = TransactionType.EXPENSE)) - insertCategory(Category(name = "居住", type = TransactionType.EXPENSE)) - insertCategory(Category(name = "医疗", type = TransactionType.EXPENSE)) - insertCategory(Category(name = "教育", type = TransactionType.EXPENSE)) - insertCategory(Category(name = "其他支出", type = TransactionType.EXPENSE)) + insertCategory(Category(name = "餐饮", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_food_24dp)) + insertCategory(Category(name = "交通", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_taxi_24dp)) + insertCategory(Category(name = "购物", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_supermarket_24dp)) + insertCategory(Category(name = "娱乐", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_bar_24dp)) + insertCategory(Category(name = "居住", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_hotel_24dp)) + insertCategory(Category(name = "医疗", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_medicine_24dp)) + insertCategory(Category(name = "教育", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_training_24dp)) + insertCategory(Category(name = "宠物", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_pet_24dp)) + insertCategory(Category(name = "花卉", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_flower_24dp)) + insertCategory(Category(name = "酒吧", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_bar_24dp)) + insertCategory(Category(name = "快递", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_delivery_24dp)) + insertCategory(Category(name = "数码", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_digital_24dp)) + insertCategory(Category(name = "化妆品", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_cosmetics_24dp)) + insertCategory(Category(name = "水果", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_fruit_24dp)) + insertCategory(Category(name = "零食", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_snack_24dp)) + insertCategory(Category(name = "蔬菜", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_vegetable_24dp)) + insertCategory(Category(name = "其他支出", type = TransactionType.EXPENSE, icon = R.drawable.ic_category_more_24dp)) // 收入分类 - insertCategory(Category(name = "工资", type = TransactionType.INCOME)) - insertCategory(Category(name = "奖金", type = TransactionType.INCOME)) - insertCategory(Category(name = "投资", type = TransactionType.INCOME)) - insertCategory(Category(name = "其他收入", type = TransactionType.INCOME)) + insertCategory(Category(name = "工资", type = TransactionType.INCOME, icon = R.drawable.ic_category_membership_24dp)) + insertCategory(Category(name = "奖金", type = TransactionType.INCOME, icon = R.drawable.ic_category_gift_24dp)) + insertCategory(Category(name = "投资", type = TransactionType.INCOME, icon = R.drawable.ic_category_digital_24dp)) + insertCategory(Category(name = "礼物", type = TransactionType.INCOME, icon = R.drawable.ic_category_gift_24dp)) + insertCategory(Category(name = "会员费", type = TransactionType.INCOME, icon = R.drawable.ic_category_membership_24dp)) + insertCategory(Category(name = "其他收入", type = TransactionType.INCOME, icon = R.drawable.ic_category_more_24dp)) } + Log.d(TAG, "Default data initialized successfully") } catch (e: Exception) { diff --git a/app/src/main/java/com/yovinchen/bookkeeping/model/Category.kt b/app/src/main/java/com/yovinchen/bookkeeping/model/Category.kt index 0c53f7a..1fa83c3 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/model/Category.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/model/Category.kt @@ -8,5 +8,6 @@ data class Category( @PrimaryKey(autoGenerate = true) val id: Long = 0, val name: String, - val type: TransactionType + val type: TransactionType, + val icon: Int? = null ) diff --git a/app/src/main/java/com/yovinchen/bookkeeping/model/Member.kt b/app/src/main/java/com/yovinchen/bookkeeping/model/Member.kt index e2359b5..08179b7 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/model/Member.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/model/Member.kt @@ -8,5 +8,6 @@ data class Member( @PrimaryKey(autoGenerate = true) val id: Int = 0, val name: String, - val description: String = "" // 可选的描述信息 + val description: String = "", // 可选的描述信息 + val icon: Int? = null // 新增icon字段,可为空 ) diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/components/RecordItem.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/components/RecordItem.kt index cf40b1a..ec9bb5f 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/ui/components/RecordItem.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/components/RecordItem.kt @@ -6,10 +6,14 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import com.yovinchen.bookkeeping.model.BookkeepingRecord import com.yovinchen.bookkeeping.model.Member import com.yovinchen.bookkeeping.model.TransactionType +import com.yovinchen.bookkeeping.utils.IconManager import java.text.SimpleDateFormat import java.util.* @@ -24,6 +28,7 @@ fun RecordItem( var showDeleteDialog by remember { mutableStateOf(false) } val timeFormat = remember { SimpleDateFormat("HH:mm", Locale.getDefault()) } val member = members.find { it.id == record.memberId } + val categoryIcon = IconManager.getCategoryIconVector(record.category) Card( modifier = modifier @@ -36,9 +41,20 @@ fun RecordItem( modifier = Modifier .fillMaxWidth() .padding(16.dp), - horizontalArrangement = Arrangement.SpaceBetween, + horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically ) { + // 左侧分类图标 + if (categoryIcon != null) { + Icon( + imageVector = categoryIcon, + contentDescription = record.category, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + } + + // 中间内容区域 Column( modifier = Modifier.weight(1f) ) { @@ -64,7 +80,7 @@ fun RecordItem( ) } - // 金额显示 + // 右侧金额显示 Text( text = String.format("%.2f", record.amount), style = MaterialTheme.typography.titleMedium, diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/CategoryManagementDialog.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/CategoryManagementDialog.kt index db63cc8..66b7167 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/CategoryManagementDialog.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/CategoryManagementDialog.kt @@ -1,38 +1,23 @@ package com.yovinchen.bookkeeping.ui.dialog -import android.util.Log import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Delete -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import com.yovinchen.bookkeeping.model.Category import com.yovinchen.bookkeeping.model.TransactionType +import com.yovinchen.bookkeeping.utils.IconManager private const val TAG = "CategoryManagementDialog" @@ -41,233 +26,217 @@ private const val TAG = "CategoryManagementDialog" fun CategoryManagementDialog( onDismiss: () -> Unit, categories: List, - onAddCategory: (String, TransactionType) -> Unit, + onAddCategory: (String, TransactionType, Int?) -> Unit, onDeleteCategory: (Category) -> Unit, - onUpdateCategory: (Category, String) -> Unit, + onUpdateCategory: (Category, String, Int?) -> Unit, selectedType: TransactionType, onTypeChange: (TransactionType) -> Unit ) { - var newCategoryName by remember { mutableStateOf("") } - var showDialog by remember { mutableStateOf(true) } - var showDeleteDialog by remember { mutableStateOf(false) } - var showEditDialog by remember { mutableStateOf(false) } - var selectedCategory: Category? by remember { mutableStateOf(null) } - var editingCategoryName by remember { mutableStateOf("") } - val filteredCategories = categories.filter { it.type == selectedType } + var showAddDialog by remember { mutableStateOf(false) } + var editingCategory by remember { mutableStateOf(null) } - Log.d(TAG, "Dialog state - showDialog: $showDialog, showDeleteDialog: $showDeleteDialog") - Log.d(TAG, "Selected category: ${selectedCategory?.name}") - - if (showDialog) { - AlertDialog( - onDismissRequest = { - Log.d(TAG, "Main dialog dismiss requested") - showDialog = false - onDismiss() - }, - title = { Text("类别管理") }, - text = { - Column( + AlertDialog( + onDismissRequest = onDismiss, + title = { Text("类别管理") }, + text = { + Column { + // 类型选择器 + Row( modifier = Modifier .fillMaxWidth() - .padding(vertical = 8.dp) + .padding(bottom = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - // 类型选择 - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly - ) { + TransactionType.values().forEach { type -> FilterChip( - selected = selectedType == TransactionType.EXPENSE, - onClick = { - Log.d(TAG, "Switching to EXPENSE type") - onTypeChange(TransactionType.EXPENSE) - }, - label = { Text("支出") } - ) - FilterChip( - selected = selectedType == TransactionType.INCOME, - onClick = { - Log.d(TAG, "Switching to INCOME type") - onTypeChange(TransactionType.INCOME) - }, - label = { Text("收入") } - ) - } - - Spacer(modifier = Modifier.height(16.dp)) - - // 添加新类别 - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - OutlinedTextField( - value = newCategoryName, - onValueChange = { newCategoryName = it }, - label = { Text("新类别名称") }, - modifier = Modifier.weight(1f) - ) - Spacer(modifier = Modifier.width(8.dp)) - IconButton( - onClick = { - if (newCategoryName.isNotBlank()) { - Log.d(TAG, "Adding new category: $newCategoryName") - onAddCategory(newCategoryName, selectedType) - newCategoryName = "" - } + selected = type == selectedType, + onClick = { onTypeChange(type) }, + label = { + Text(when (type) { + TransactionType.EXPENSE -> "支出" + TransactionType.INCOME -> "收入" + }) } - ) { - Icon(Icons.Default.Add, contentDescription = "添加类别") - } + ) } + } - Spacer(modifier = Modifier.height(16.dp)) - - // 类别列表 - LazyColumn( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - items(filteredCategories) { category -> + // 类别列表 + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + items(categories.filter { it.type == selectedType }) { category -> + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + .clickable { editingCategory = category }, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) ) { - Text( - text = category.name, - modifier = Modifier - .weight(1f) - .clickable { - selectedCategory = category - editingCategoryName = category.name - showEditDialog = true - } - ) - IconButton( - onClick = { - Log.d(TAG, "Selected category for deletion: ${category.name}") - selectedCategory = category - showDeleteDialog = true + // 显示类别图标 + if (category.icon != null) { + Icon( + imageVector = ImageVector.vectorResource(id = category.icon), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + } else { + IconManager.getCategoryIconVector(category.name)?.let { icon -> + Icon( + imageVector = icon, + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) } - ) { - Icon(Icons.Default.Delete, contentDescription = "删除类别") } + + Spacer(modifier = Modifier.width(8.dp)) + Text(category.name) + } + + IconButton( + onClick = { onDeleteCategory(category) }, + enabled = categories.size > 1 + ) { + Icon( + Icons.Default.Delete, + contentDescription = "删除", + tint = if (categories.size > 1) + MaterialTheme.colorScheme.error + else + MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) + ) } } } } - }, - confirmButton = { - TextButton( - onClick = { - Log.d(TAG, "Main dialog confirmed") - showDialog = false - onDismiss() - } + + // 添加类别按钮 + Button( + onClick = { showAddDialog = true }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) ) { - Text("完成") + Icon(Icons.Default.Add, contentDescription = null) + Spacer(modifier = Modifier.width(8.dp)) + Text("添加类别") } } - ) - } + }, + confirmButton = { + TextButton(onClick = onDismiss) { + Text("完成") + } + } + ) - // 删除确认对话框 - if (showDeleteDialog && selectedCategory != null) { - AlertDialog( - onDismissRequest = { - Log.d(TAG, "Delete dialog dismissed") - showDeleteDialog = false - selectedCategory = null - }, - title = { Text("确认删除") }, - text = { - Text( - text = buildString { - append("确定要删除类别 ") - append(selectedCategory?.name ?: "") - append(" 吗?") - } - ) - }, - confirmButton = { - TextButton( - onClick = { - try { - selectedCategory?.let { category -> - Log.d(TAG, "Confirming deletion of category: ${category.name}") - onDeleteCategory(category) - } - } catch (e: Exception) { - Log.e(TAG, "Error during category deletion callback", e) - e.printStackTrace() - } finally { - showDeleteDialog = false - selectedCategory = null - } - } - ) { - Text("确定") - } - }, - dismissButton = { - TextButton( - onClick = { - Log.d(TAG, "Canceling deletion") - showDeleteDialog = false - selectedCategory = null - } - ) { - Text("取消") - } + // 添加类别对话框 + if (showAddDialog) { + CategoryEditDialog( + onDismiss = { showAddDialog = false }, + onConfirm = { name, iconResId -> + onAddCategory(name, selectedType, iconResId) + showAddDialog = false } ) } // 编辑类别对话框 - if (showEditDialog && selectedCategory != null) { - AlertDialog( - onDismissRequest = { - showEditDialog = false - selectedCategory = null - editingCategoryName = "" + editingCategory?.let { category -> + CategoryEditDialog( + onDismiss = { editingCategory = null }, + onConfirm = { name, iconResId -> + onUpdateCategory(category, name, iconResId) + editingCategory = null }, - title = { Text("编辑类别") }, - text = { - OutlinedTextField( - value = editingCategoryName, - onValueChange = { editingCategoryName = it }, - label = { Text("类别名称") } - ) - }, - confirmButton = { - TextButton( - onClick = { - if (editingCategoryName.isNotBlank()) { - selectedCategory?.let { category -> - onUpdateCategory(category, editingCategoryName) - } - } - showEditDialog = false - selectedCategory = null - editingCategoryName = "" - } - ) { - Text("确定") - } - }, - dismissButton = { - TextButton( - onClick = { - showEditDialog = false - selectedCategory = null - editingCategoryName = "" - } - ) { - Text("取消") - } - } + initialName = category.name, + initialIcon = category.icon + ) + } +} + +@Composable +private fun CategoryEditDialog( + onDismiss: () -> Unit, + onConfirm: (String, Int?) -> Unit, + initialName: String = "", + initialIcon: Int? = null +) { + var name by remember { mutableStateOf(initialName) } + var selectedIcon by remember { mutableStateOf(initialIcon) } + var showIconPicker by remember { mutableStateOf(false) } + + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(if (initialName.isEmpty()) "添加类别" else "编辑类别") }, + text = { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + OutlinedTextField( + value = name, + onValueChange = { name = it }, + label = { Text("名称") }, + modifier = Modifier.fillMaxWidth() + ) + + // 图标选择按钮 + Button( + onClick = { showIconPicker = true }, + modifier = Modifier.fillMaxWidth() + ) { + selectedIcon?.let { iconResId -> + Icon( + imageVector = ImageVector.vectorResource(id = iconResId), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + Spacer(modifier = Modifier.width(8.dp)) + } + Text(if (selectedIcon == null) "选择图标" else "更改图标") + } + } + }, + confirmButton = { + TextButton( + onClick = { + if (name.isNotBlank()) { + onConfirm(name, selectedIcon) + } + } + ) { + Text("确定") + } + }, + dismissButton = { + TextButton(onClick = onDismiss) { + Text("取消") + } + } + ) + + if (showIconPicker) { + IconPickerDialog( + onDismiss = { showIconPicker = false }, + onIconSelected = { + selectedIcon = it + showIconPicker = false + }, + selectedIcon = selectedIcon, + isMemberIcon = false, + title = "选择类别图标" ) } } diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/IconPickerDialog.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/IconPickerDialog.kt new file mode 100644 index 0000000..086f412 --- /dev/null +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/IconPickerDialog.kt @@ -0,0 +1,61 @@ +package com.yovinchen.bookkeeping.ui.dialog + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import com.yovinchen.bookkeeping.utils.IconManager + +@Composable +fun IconPickerDialog( + onDismiss: () -> Unit, + onIconSelected: (Int) -> Unit, + selectedIcon: Int? = null, + isMemberIcon: Boolean = false, + title: String = "选择图标" +) { + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(title) }, + text = { + LazyVerticalGrid( + columns = GridCells.Fixed(4), + modifier = Modifier + .fillMaxWidth() + .height(300.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + val icons = if (isMemberIcon) { + IconManager.getAllMemberIcons() + } else { + IconManager.getAllCategoryIcons() + } + + items(icons) { iconResId -> + Icon( + imageVector = ImageVector.vectorResource(id = iconResId), + contentDescription = null, + modifier = Modifier + .size(40.dp) + .clickable { onIconSelected(iconResId) }, + tint = if (selectedIcon == iconResId) MaterialTheme.colorScheme.primary else Color.Unspecified + ) + } + } + }, + confirmButton = { + TextButton(onClick = onDismiss) { + Text("取消") + } + } + ) +} diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/MemberManagementDialog.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/MemberManagementDialog.kt index 8edb7b1..02e3d88 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/MemberManagementDialog.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/dialog/MemberManagementDialog.kt @@ -7,20 +7,25 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Person import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import com.yovinchen.bookkeeping.model.Member +import com.yovinchen.bookkeeping.utils.IconManager @Composable fun MemberManagementDialog( onDismiss: () -> Unit, members: List, - onAddMember: (String, String) -> Unit, + onAddMember: (String, String, Int?) -> Unit, onDeleteMember: (Member) -> Unit, - onUpdateMember: (Member, String, String) -> Unit + onUpdateMember: (Member, String, String, Int?) -> Unit ) { var showAddDialog by remember { mutableStateOf(false) } var selectedMember by remember { mutableStateOf(null) } @@ -43,41 +48,65 @@ fun MemberManagementDialog( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - Column(modifier = Modifier.weight(1f)) { - Text( - text = member.name, - style = MaterialTheme.typography.titleMedium - ) - if (member.description.isNotEmpty()) { - Text( - text = member.description, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) + ) { + // 显示成员图标 + if (member.icon != null) { + Icon( + imageVector = ImageVector.vectorResource(member.icon), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified ) + } else { + IconManager.getMemberIconVector(member.name)?.let { icon -> + Icon( + imageVector = icon, + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + } + } + + Spacer(modifier = Modifier.width(8.dp)) + + Column { + Text( + text = member.name, + style = MaterialTheme.typography.titleMedium + ) + if (member.description.isNotEmpty()) { + Text( + text = member.description, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } } } + Row { IconButton(onClick = { selectedMember = member }) { - Icon(Icons.Default.Edit, "编辑成员") + Icon(Icons.Default.Edit, contentDescription = "编辑") } IconButton(onClick = { onDeleteMember(member) }) { - Icon(Icons.Default.Delete, "删除成员") + Icon(Icons.Default.Delete, contentDescription = "删除") } } } - if (members.indexOf(member) < members.size - 1) { - HorizontalDivider() - } } } - + Button( onClick = { showAddDialog = true }, modifier = Modifier .fillMaxWidth() - .padding(top = 16.dp) + .padding(top = 8.dp) ) { - Icon(Icons.Default.Add, "添加成员") + Icon(Icons.Default.Add, contentDescription = null) Spacer(modifier = Modifier.width(8.dp)) Text("添加成员") } @@ -85,32 +114,31 @@ fun MemberManagementDialog( }, confirmButton = { TextButton(onClick = onDismiss) { - Text("关闭") + Text("完成") } } ) - // 添加成员对话框 if (showAddDialog) { MemberEditDialog( onDismiss = { showAddDialog = false }, - onConfirm = { name, description -> - onAddMember(name, description) + onConfirm = { name, description, iconResId -> + onAddMember(name, description, iconResId) showAddDialog = false } ) } - // 编辑成员对话框 selectedMember?.let { member -> MemberEditDialog( onDismiss = { selectedMember = null }, - onConfirm = { name, description -> - onUpdateMember(member, name, description) + onConfirm = { name, description, iconResId -> + onUpdateMember(member, name, description, iconResId) selectedMember = null }, initialName = member.name, - initialDescription = member.description + initialDescription = member.description, + initialIcon = member.icon ) } } @@ -118,45 +146,63 @@ fun MemberManagementDialog( @Composable private fun MemberEditDialog( onDismiss: () -> Unit, - onConfirm: (String, String) -> Unit, + onConfirm: (String, String, Int?) -> Unit, initialName: String = "", - initialDescription: String = "" + initialDescription: String = "", + initialIcon: Int? = null ) { var name by remember { mutableStateOf(initialName) } var description by remember { mutableStateOf(initialDescription) } + var selectedIcon by remember { mutableStateOf(initialIcon) } + var showIconPicker by remember { mutableStateOf(false) } AlertDialog( onDismissRequest = onDismiss, title = { Text(if (initialName.isEmpty()) "添加成员" else "编辑成员") }, text = { Column( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp) + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(8.dp) ) { OutlinedTextField( value = name, onValueChange = { name = it }, - label = { Text("成员名称") }, + label = { Text("名称") }, modifier = Modifier.fillMaxWidth() ) - Spacer(modifier = Modifier.height(16.dp)) + OutlinedTextField( value = description, onValueChange = { description = it }, - label = { Text("描述(可选)") }, + label = { Text("描述") }, modifier = Modifier.fillMaxWidth() ) + + // 图标选择按钮 + Button( + onClick = { showIconPicker = true }, + modifier = Modifier.fillMaxWidth() + ) { + selectedIcon?.let { iconResId -> + Icon( + imageVector = ImageVector.vectorResource(iconResId), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + Spacer(modifier = Modifier.width(8.dp)) + } + Text(if (selectedIcon == null) "选择图标" else "更改图标") + } } }, confirmButton = { TextButton( onClick = { if (name.isNotBlank()) { - onConfirm(name.trim(), description.trim()) + onConfirm(name, description, selectedIcon) } - }, - enabled = name.isNotBlank() + } ) { Text("确定") } @@ -167,4 +213,17 @@ private fun MemberEditDialog( } } ) + + if (showIconPicker) { + IconPickerDialog( + onDismiss = { showIconPicker = false }, + onIconSelected = { + selectedIcon = it + showIconPicker = false + }, + selectedIcon = selectedIcon, + isMemberIcon = true, + title = "选择成员图标" + ) + } } diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/navigation/MainNavigation.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/navigation/MainNavigation.kt index aa7eb0f..fa746af 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/ui/navigation/MainNavigation.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/navigation/MainNavigation.kt @@ -1,20 +1,13 @@ package com.yovinchen.bookkeeping.ui.navigation import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.List -import androidx.compose.material.icons.filled.Analytics -import androidx.compose.material.icons.filled.Settings -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -22,20 +15,38 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument +import com.yovinchen.bookkeeping.R import com.yovinchen.bookkeeping.model.AnalysisType import com.yovinchen.bookkeeping.model.ThemeMode import com.yovinchen.bookkeeping.ui.screen.* +import androidx.compose.material3.* +import androidx.compose.ui.unit.dp import java.time.YearMonth import java.time.format.DateTimeFormatter sealed class Screen( val route: String, val title: String, - val icon: ImageVector? = null + val iconResId: Int? = null ) { - object Home : Screen("home", "记账", Icons.AutoMirrored.Filled.List) - object Analysis : Screen("analysis", "分析", Icons.Default.Analytics) - object Settings : Screen("settings", "设置", Icons.Default.Settings) + @Composable + fun icon(): ImageVector? = iconResId?.let { ImageVector.vectorResource(it) } + + object Home : Screen( + "home", + "记账", + iconResId = R.drawable.account + ) + object Analysis : Screen( + "analysis", + "分析", + iconResId = R.drawable.piechart + ) + object Settings : Screen( + "settings", + "设置", + iconResId = R.drawable.setting + ) object CategoryDetail : Screen( "category_detail/{category}/{startMonth}/{endMonth}", "分类详情" @@ -75,18 +86,33 @@ fun MainNavigation( onThemeChange: (ThemeMode) -> Unit ) { val navController = rememberNavController() - + val items = listOf( + Screen.Home, + Screen.Analysis, + Screen.Settings + ) + Scaffold( bottomBar = { NavigationBar { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route - - Screen.bottomNavigationItems().forEach { screen -> + + items.forEach { screen -> + val selected = currentRoute == screen.route NavigationBarItem( - icon = { Icon(screen.icon!!, contentDescription = screen.title) }, + icon = { + screen.icon()?.let { icon -> + Icon( + imageVector = icon, + contentDescription = screen.title, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + } + }, label = { Text(screen.title) }, - selected = currentRoute == screen.route, + selected = selected, onClick = { navController.navigate(screen.route) { popUpTo(navController.graph.findStartDestination().id) { diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/SettingsScreen.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/SettingsScreen.kt index eff0a53..2284f99 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/SettingsScreen.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/SettingsScreen.kt @@ -152,9 +152,13 @@ fun SettingsScreen( CategoryManagementDialog( onDismiss = { showCategoryDialog = false }, categories = categories, - onAddCategory = viewModel::addCategory, + onAddCategory = { name, type, iconResId -> + viewModel.addCategory(name, type, iconResId) + }, onDeleteCategory = viewModel::deleteCategory, - onUpdateCategory = viewModel::updateCategory, + onUpdateCategory = { category, newName, iconResId -> + viewModel.updateCategory(category, newName, iconResId) + }, selectedType = selectedType, onTypeChange = viewModel::setSelectedCategoryType ) @@ -165,10 +169,16 @@ fun SettingsScreen( MemberManagementDialog( onDismiss = { showMemberDialog = false }, members = members, - onAddMember = memberViewModel::addMember, + onAddMember = { name, description, iconResId -> + memberViewModel.addMember(name, description, iconResId) + }, onDeleteMember = memberViewModel::deleteMember, - onUpdateMember = { member, name, description -> - memberViewModel.updateMember(member.copy(name = name, description = description)) + onUpdateMember = { member, name, description, iconResId -> + memberViewModel.updateMember(member.copy( + name = name, + description = description, + icon = iconResId + )) } ) } diff --git a/app/src/main/java/com/yovinchen/bookkeeping/utils/IconManager.kt b/app/src/main/java/com/yovinchen/bookkeeping/utils/IconManager.kt new file mode 100644 index 0000000..faa54a3 --- /dev/null +++ b/app/src/main/java/com/yovinchen/bookkeeping/utils/IconManager.kt @@ -0,0 +1,84 @@ +package com.yovinchen.bookkeeping.utils + +import androidx.annotation.DrawableRes +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import com.yovinchen.bookkeeping.R + +object IconManager { + // 类别图标映射 + private val categoryIcons = mapOf( + "食品" to R.drawable.ic_category_food_24dp, + "交通" to R.drawable.ic_category_taxi_24dp, + "娱乐" to R.drawable.ic_category_bar_24dp, + "购物" to R.drawable.ic_category_supermarket_24dp, + "工资" to R.drawable.ic_category_membership_24dp, + "服装" to R.drawable.ic_category_clothes_24dp, + "数码" to R.drawable.ic_category_digital_24dp, + "饮料" to R.drawable.ic_category_drink_24dp, + "医疗" to R.drawable.ic_category_medicine_24dp, + "旅行" to R.drawable.ic_category_travel_24dp, + "便利店" to R.drawable.ic_category_convenience_24dp, + "化妆品" to R.drawable.ic_category_cosmetics_24dp, + "外卖" to R.drawable.ic_category_delivery_24dp, + "鲜花" to R.drawable.ic_category_flower_24dp, + "水果" to R.drawable.ic_category_fruit_24dp, + "礼物" to R.drawable.ic_category_gift_24dp, + "住宿" to R.drawable.ic_category_hotel_24dp, + "宠物" to R.drawable.ic_category_pet_24dp, + "景点" to R.drawable.ic_category_scenic_24dp, + "零食" to R.drawable.ic_category_snack_24dp, + "培训" to R.drawable.ic_category_training_24dp, + "蔬菜" to R.drawable.ic_category_vegetable_24dp, + "婴儿" to R.drawable.ic_category_baby_24dp, + "餐饮" to R.drawable.ic_category_food_24dp, // 添加餐饮分类 + "居住" to R.drawable.ic_category_hotel_24dp, // 添加居住分类 + "其他" to R.drawable.ic_category_more_24dp + ) + + // 成员图标映射 + private val memberIcons = mapOf( + "自己" to R.drawable.ic_member_boy_24dp, + "家庭" to R.drawable.ic_member_family_24dp, + "父亲" to R.drawable.ic_member_father_24dp, + "母亲" to R.drawable.ic_member_mother_24dp, + "男宝" to R.drawable.ic_member_baby_boy_24dp, + "女宝" to R.drawable.ic_member_baby_girl_24dp, + "新娘" to R.drawable.ic_member_bride_24dp, + "新郎" to R.drawable.ic_member_groom_24dp, + "爷爷" to R.drawable.ic_member_grandfather_24dp, + "奶奶" to R.drawable.ic_member_grandmother_24dp, + "男生" to R.drawable.ic_member_boy_24dp, + "女生" to R.drawable.ic_member_girl_24dp, + "其他" to R.drawable.ic_member_girl_24dp + ) + + @Composable + fun getCategoryIconVector(name: String): ImageVector? { + return categoryIcons[name]?.let { ImageVector.vectorResource(id = it) } + } + + @Composable + fun getMemberIconVector(name: String): ImageVector? { + return memberIcons[name]?.let { ImageVector.vectorResource(id = it) } + } + + @DrawableRes + fun getCategoryIcon(name: String): Int? { + return categoryIcons[name] + } + + @DrawableRes + fun getMemberIcon(name: String): Int? { + return memberIcons[name] + } + + fun getAllCategoryIcons(): List { + return categoryIcons.values.toList() + } + + fun getAllMemberIcons(): List { + return memberIcons.values.toList() + } +} diff --git a/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/HomeViewModel.kt b/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/HomeViewModel.kt index 3e9d9e8..daebbf3 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/HomeViewModel.kt @@ -1,7 +1,6 @@ package com.yovinchen.bookkeeping.viewmodel import android.app.Application -import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.yovinchen.bookkeeping.data.BookkeepingDatabase @@ -19,7 +18,6 @@ import java.util.* @OptIn(ExperimentalCoroutinesApi::class) class HomeViewModel(application: Application) : AndroidViewModel(application) { - private val TAG = "HomeViewModel" private val bookkeepingDao = BookkeepingDatabase.getDatabase(application).bookkeepingDao() private val memberDao = BookkeepingDatabase.getDatabase(application).memberDao() private val categoryDao = BookkeepingDatabase.getDatabase(application).categoryDao() diff --git a/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/MemberViewModel.kt b/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/MemberViewModel.kt index f4b84cc..d52cb8e 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/MemberViewModel.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/MemberViewModel.kt @@ -13,9 +13,9 @@ class MemberViewModel(application: Application) : AndroidViewModel(application) val allMembers: Flow> = memberDao.getAllMembers() - fun addMember(name: String, description: String = "") { + fun addMember(name: String, description: String = "", iconResId: Int? = null) { viewModelScope.launch { - val member = Member(name = name, description = description) + val member = Member(name = name, description = description, icon = iconResId) memberDao.insertMember(member) } } diff --git a/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/SettingsViewModel.kt b/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/SettingsViewModel.kt index 37f3c16..9900291 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/SettingsViewModel.kt @@ -32,9 +32,9 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application _selectedCategoryType.value = type } - fun addCategory(name: String, type: TransactionType) { + fun addCategory(name: String, type: TransactionType, iconResId: Int?) { viewModelScope.launch { - val category = Category(name = name, type = type) + val category = Category(name = name, type = type, icon = iconResId) dao.insertCategory(category) } } @@ -45,9 +45,9 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application } } - fun updateCategory(category: Category, newName: String) { + fun updateCategory(category: Category, newName: String, iconResId: Int?) { viewModelScope.launch { - val updatedCategory = category.copy(name = newName) + val updatedCategory = category.copy(name = newName, icon = iconResId) dao.updateCategory(updatedCategory) // 更新所有使用该类别的记录 dao.updateRecordCategories(category.name, newName) diff --git a/app/src/main/res/drawable/account.xml b/app/src/main/res/drawable/account.xml index 9af4df4..4d43137 100644 --- a/app/src/main/res/drawable/account.xml +++ b/app/src/main/res/drawable/account.xml @@ -1,6 +1,6 @@