1. 编辑笔记
- EditNotesScreen:这是一个可编辑笔记的屏幕,它接收一个NavController对象、一个书籍的bookId和一个可选的modifier参数。它使用了LocalNotesViewModel来管理笔记的数据。
- 它首先定义了几个状态变量,包括是否显示对话框、编辑内容和当前正在编辑的笔记ID。
- 使用LaunchedEffect来通知viewModel更新当前的bookId。
- 使用Scaffold构建UI布局,包括一个悬浮按钮,用于触发添加新笔记的操作。
- 在Column中构建了屏幕的主体布局,包括一个返回箭头图标和标题文本。
- 如果showDialog为真,则显示一个AlertDialog,用于添加或编辑笔记。对话框中包含一个文本字段和一个确认按钮,用于提交笔记内容。
- 使用observeAsState观察LiveData,获取当前书籍的所有笔记,并将其转换为Compose可用的状态。
- 最后,调用NotesListScreen来显示笔记列表,允许用户点击笔记进行编辑或删除。
- NotesListScreen:这是一个显示笔记列表的Composable函数,它接收一个笔记列表notes,以及点击笔记时的回调onNoteClick和删除笔记时的回调onDeleteClick。
- 使用LazyColumn构建一个懒加载的列表,为每条笔记创建一个Card组件。
- 在每个Card中,使用Row布局显示笔记内容和一个删除按钮。笔记内容部分是可点击的,当点击时会调用onNoteClick回调。删除按钮点击时会调用onDeleteClick回调。
app/src/main/java/com/example/BookRecord/EditNoteScreen.kt:
@Composable
fun EditNotesScreen(
navController: NavController,
bookId: Int,
modifier: Modifier = Modifier,
){
var showDialog by remember { mutableStateOf(false) }
var editingContent by remember { mutableStateOf("") }
var currentEditingNoteId by remember { mutableStateOf<Int?>(null) }
val viewModel = LocalNotesViewModel.current
// 通知 ViewModel 更新当前 bookId
LaunchedEffect(bookId) {
viewModel.setBookId(bookId)
}
Scaffold(
floatingActionButton = {
SmallAddButton(onClick = {
// 在这里定义点击悬浮按钮后的动作
editingContent = ""
currentEditingNoteId = null // 表示添加新笔记
showDialog = true
})
},
floatingActionButtonPosition = FabPosition.End, // 将按钮放在右下角
){ paddingValues ->
Column(
modifier = Modifier
.padding(paddingValues)
.padding(16.dp)
){
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically // 垂直居中对齐 Row 内的元素
) {
Column(modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally){
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF6650a4),
modifier = Modifier
.size(40.dp)
.clickable {
navController.navigate("Book")
}
)
}
Column(modifier = Modifier.weight(9f)) {
Text(
text = "Edit Notes",
fontSize = 25.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF6650a4),
modifier = Modifier.padding(start = 20.dp) // 根据需要调整文本的右边距
)
}
}
// 显示添加或编辑笔记的对话框
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text(if (currentEditingNoteId == null) "Add Note" else "Edit Note") },
text = {
TextField(
value = editingContent,
onValueChange = { editingContent = it },
label = { Text("Content") }
)
},
confirmButton = {
Button(
onClick = {
if (currentEditingNoteId == null) {
// 添加新笔记
viewModel.addNote(editingContent, bookId = bookId)
} else {
// 更新现有笔记
currentEditingNoteId?.let { noteId ->
// 注意:这里同样假设 bookId 不会是 null,以及你有一个接收 noteId 和 noteContent 的 editNote 方法。
// 如果你的 editNote 方法需要不同的参数,请相应地调整。
viewModel.editNote(noteId = noteId, editingContent, bookId = bookId) // 如果 bookId 是 null,则需要决定如何处理
}
}
showDialog = false
}
) { Text("OK") }
},
dismissButton = {
Button(onClick = { showDialog = false }) { Text("Cancel") }
}
)
}
// 观察 LiveData 并将其转换为 Compose 可用的状态
val notesByBookId = viewModel.notesByBookId.observeAsState(initial = emptyList())
// 笔记列表...
NotesListScreen(
notes = notesByBookId.value,
onNoteClick = { note ->
editingContent = note.content
currentEditingNoteId = note.id
showDialog = true
},
onDeleteClick = { note ->
viewModel.deleteNote(note)
}
)
}
}
}
@Composable
fun NotesListScreen(
notes: List<Note>,
onNoteClick: (Note) -> Unit,
onDeleteClick: (Note) -> Unit
) {
LazyColumn {
items(items = notes, key = { it.id }) { note ->
Card(
modifier = Modifier
.padding(4.dp)
.fillMaxWidth()
) {
Row(modifier = Modifier
.padding(8.dp)
.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(
text = note.content,
modifier = Modifier
.weight(1f)
.clickable { onNoteClick(note) }
)
IconButton(onClick = { onDeleteClick(note) }) { // 当点击删除按钮时,调用 onDeleteClick
Icon(Icons.Default.Delete, contentDescription = "Delete")
}
}
}
}
}
}
2. 展示笔记
- NotesScreen:这是一个用于查看笔记的屏幕,它接收一个NavController对象、一个书籍的bookId和一个可选的modifier参数。它使用了LocalNotesViewModel来管理笔记的数据。
- 它首先定义了一个viewModel实例,用于获取和更新笔记数据。
- 使用LaunchedEffect来通知viewModel更新当前的bookId,这样viewModel就可以加载与该书籍相关的笔记。
- 使用Scaffold构建UI布局,这是Compose中的一个主要布局组件,用于提供材料设计的基础结构。
- 在Column中构建了屏幕的主体布局,包括一个返回箭头图标和标题文本“View Notes”。
- 返回箭头图标是可点击的,当用户点击时,它将使用NavController导航回到“Bookshelf”路由。
- NoteList Composable函数被用来显示笔记列表,它接收一个笔记列表notes和一个modifier参数。
- NoteList:这是一个用于显示笔记列表的Composable函数,它接收一个笔记列表notes和一个可选的modifier参数。
- 使用LazyColumn构建一个懒加载的列表,这是Compose中的一个性能优化组件,用于高效地显示大量列表项。
- 列表中的每个项目都是一个Card组件,它显示了单个笔记的内容。
- Column用于在卡片内部创建一个垂直布局,其中包含笔记的文本内容。
app/src/main/java/com/example/BookRecord/NotesScreen.kt:
@Composable
fun NotesScreen( // 重命名为 NotesScreen
navController: NavController,
bookId:Int,
modifier: Modifier = Modifier,
) {
// 示例笔记数据
val viewModel = LocalNotesViewModel.current
// 通知 ViewModel 更新当前 bookId
LaunchedEffect(bookId) {
viewModel.setBookId(bookId)
}
// 观察 LiveData 并将其转换为 Compose 可用的状态
val notesByBookId = viewModel.notesByBookId.observeAsState(initial = emptyList())
Scaffold() {paddingValues ->
Column(
modifier = modifier
.padding(paddingValues)
){
Row(
modifier = Modifier.fillMaxWidth()
.padding(0.dp),
verticalAlignment = Alignment.CenterVertically // 垂直居中对齐 Row 内的元素
){
Column(modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally){
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF6650a4),
modifier = Modifier
.size(40.dp)
.clickable {
navController.navigate("Bookshelf")
}
)
}
Column(modifier = Modifier.weight(9f)) {
Text(
text = "View Notes",
fontSize = 25.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF6650a4),
modifier = Modifier.padding(start = 20.dp) // 根据需要调整文本的右边距
)
}
}
// 笔记列表
NoteList(notes = notesByBookId.value, modifier = modifier.padding(paddingValues).padding(16.dp))
}
}
}
@Composable
fun NoteList(notes: List<Note>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
items(items = notes, key = { note -> note.hashCode() }) { note ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(text = note.content)
}
}
}
}
}
3. NoteViewModel.
-
构造函数:接收一个 Application 对象,并在初始化块中创建 NoteRepository 实例。NoteRepository 用于访问数据库和执行数据库操作。
-
数据库访问:通过 AppDatabase.getDatabase(application) 获取数据库实例,然后通过 noteDao() 方法获取 NotesDao 实例,这是与笔记表交互的接口。
-
当前选中的 BookId:使用 MutableLiveData<Int?> 来跟踪当前选中的书籍 ID (_currentBookId)。这个值用于确定要加载哪个书籍的笔记列表。
-
笔记列表:notesByBookId 是一个 LiveData<List> 属性,它使用 switchMap 函数根据当前的 bookId 获取对应的笔记列表。如果 bookId 为 null,则返回一个包含空列表的 MutableLiveData。
-
设置书籍 ID:setBookId 函数用于更新 _currentBookId 的值,这将触发 notesByBookId 重新获取对应书籍的笔记列表。
-
添加笔记:addNote 函数接收笔记内容和书籍 ID,然后在协程作用域内调用 repository.insert 方法将新笔记插入数据库。
-
删除笔记:deleteNote 函数接收一个 Note 对象,然后在协程作用域内调用 repository.delete 方法从数据库中删除该笔记。
-
编辑笔记:editNote 函数接收笔记 ID、更新后的笔记内容和书籍 ID,然后在协程作用域内调用 repository.update 方法更新数据库中的笔记。
-
协程处理:使用 viewModelScope.launch 在 ViewModel 的作用域内启动协程,以便在后台线程上执行数据库操作。
app/src/main/java/com/example/BookRecord/NoteViewModel.kt
package com.example.BookRecord
import android.app.Application
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.switchMap
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class NoteViewModel(application: Application) : AndroidViewModel(application) {
private val repository: NoteRepository
// 使用 MutableLiveData 来跟踪当前选中的 bookId
private val _currentBookId = MutableLiveData<Int?>()
init {
val appDatabase = AppDatabase.getDatabase(application)
val notesDao = appDatabase.noteDao()
repository = NoteRepository(notesDao)
}
// 使用 switchMap 来根据当前的 bookId 获取对应的笔记列表
val notesByBookId: LiveData<List<Note>> = _currentBookId.switchMap { bookId ->
bookId?.let {
repository.getNotesByBookId(it)
} ?: MutableLiveData(emptyList())
}
fun setBookId(bookId: Int) {
_currentBookId.value = bookId
}
// 添加笔记需要提供bookId
fun addNote(noteContent: String, bookId: Int) = viewModelScope.launch {
val newNote = Note(content = noteContent, bookId = bookId)
repository.insert(newNote)
}
// 删除笔记
fun deleteNote(note: Note) = viewModelScope.launch {
repository.delete(note)
}
// 编辑笔记
fun editNote(noteId: Int, noteContent: String, bookId: Int) = viewModelScope.launch {
val noteToUpdate = Note(id = noteId, content = noteContent, bookId = bookId)
repository.update(noteToUpdate)
}
}