1.加入图书页面
- AddBookScreen:这是一个用于添加书籍的屏幕,它接收一个NavController对象和一个可选的modifier参数。
- 它首先定义了一个focusManager,用于管理键盘操作。
- 使用mutableStateOf创建了一个状态变量searchText,用于存储搜索框中的文本。
-
Column:使用Column构建了屏幕的主体布局,并设置了内边距。
-
Row:在Column中,使用Row布局创建了一个包含返回图标和搜索框的水平布局。
- IconButton:定义了一个返回图标按钮,点击时会调用navController.popBackStack()返回上一个导航目的地。
- Spacer:在返回图标和搜索框之间添加了空间。
- OutlinedTextField:定义了一个带有轮廓的文本字段,用于输入搜索内容。
- value和onValueChange参数用于控制文本字段的值和更改时的回调。
- label提供了文本字段的标签,指示用户可以搜索书名或作者。
- modifier用于设置文本字段的背景颜色和圆角形状。
- colors参数自定义了文本字段的颜色,包括光标颜色、聚焦时的边框颜色和非聚焦时的边框颜色。
- singleLine设置为true,表示文本字段为单行输入。
- trailingIcon在文本字段的右侧显示一个清除图标,当文本字段中有内容时显示,点击可以清空文本字段。
- keyboardActions定义了键盘操作,其中onSearch用于处理搜索逻辑,同时隐藏键盘。
- keyboardOptions设置了键盘的输入模式为搜索,通常在键盘上显示一个搜索按钮。
app/src/main/java/com/example/BookRecord/AddBooks.kt:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddBookScreen(
navController: NavController,
modifier: Modifier = Modifier,
) {
// The focus manager to handle the keyboard actions
val focusManager = LocalFocusManager.current
// State for search text
var searchText by remember { mutableStateOf("") }
Column(
modifier = modifier
.padding(12.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
// Back icon with a larger touch target for better accessibility
IconButton(
onClick = { navController.popBackStack() },
modifier = Modifier.size(35.dp)
) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Back",
tint = Color(0xFF6650a4)
)
}
Spacer(modifier = Modifier.width(10.dp)) // Add space between the icon and the search bar
// Search input field
OutlinedTextField(
value = searchText,
onValueChange = { searchText = it },
label = { Text("Search by title, author", color = Color(0xFF6650a4)) },
modifier = Modifier
.fillMaxWidth()
.background(Color(0xFFF2F2F2), RoundedCornerShape(20.dp)), // 直接在这里设置背景颜色和形状
shape = RoundedCornerShape(20.dp), // 设置输入框的形状
colors = TextFieldDefaults.outlinedTextFieldColors(
// backgroundColor = Color(0xFFF2F2F2),
cursorColor = Color(0xFF6650a4),
focusedBorderColor = Color(0xFF6650a4),
unfocusedBorderColor = Color(0xFF6650a4)
),
singleLine = true,
trailingIcon = {
if (searchText.isNotEmpty()) {
IconButton(onClick = { searchText = "" }) {
Icon(
imageVector = Icons.Filled.Clear,
contentDescription = "Clear",
tint = Color(0xFF6650a4)
)
}
}
},
keyboardActions = KeyboardActions(
onSearch = {
focusManager.clearFocus() // Hide the keyboard
// TODO: Implement the search logic here
}
),
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Search)
)
}
// TODO: Add the rest of your UI components here
}
}
2. BookViewModel
-
BookStatus枚举:定义了书籍的三种状态,分别是正在阅读(READING)、已阅读(READ)和搁置(ON_HOLD)。
-
BookViewModel类:这是一个继承自AndroidViewModel的视图模型类,用于管理和存储书籍数据。
- 类的初始化块中,通过传入的Application对象获取了数据库的访问对象bookDao和noteDao,这些对象用于与数据库进行交互。
- bookRepository和noteRepository是用于数据操作的仓库类实例,它们封装了对数据库的直接访问。
- currentUserUID存储了当前登录用户的用户ID,用于将书籍与用户关联。
- allBooks是一个LiveData对象,用于观察所有书籍的数据变化。
- bookCounts是一个MutableLiveData对象,用于存储和更新书籍状态的计数。
- readingBooks、completeBooks和layasideBooks是MediatorLiveData对象,它们根据书籍的状态过滤allBooks中的数据。
- 在初始化块中,设置了MediatorLiveData的源为allBooks,并根据书籍的不同状态更新这些LiveData对象。
- updateBookCounts方法用于更新书籍状态的计数,并将其存储在bookCounts中。
- addBook方法用于添加新书籍到数据库中。
- deleteBook方法用于从数据库中删除书籍。
- updateBookStatus方法用于更新书籍的状态。
- updateBookReadPage方法用于更新书籍的已阅读页数。
- getNoteCountByBookId方法用于获取指定书籍的笔记数量。
app/src/main/java/com/example/BookRecord/BookViewModel.kt:
enum class BookStatus {
READING, // 正在阅读
READ, // 已阅读
ON_HOLD // 搁置
}
//在类的初始化块中,通过传入应用程序的 Application 对象,获取了数据库的访问对象 bookDao 和 noteDao
class BookViewModel(application: Application) : AndroidViewModel(application) {
private val bookDao = AppDatabase.getDatabase(application).bookDao()
private val noteDao = AppDatabase.getDatabase(application).noteDao()
private val bookRepository = BookRepository(bookDao, viewModelScope)
private val noteRepository = NoteRepository(noteDao)
var currentUserUID = FirebaseAuth.getInstance().currentUser?.uid
val allBooks: LiveData<List<Book>> = bookRepository.allBooks
val bookCounts = MutableLiveData<Map<String, Int>>()
// 使用 MediatorLiveData 替代 Transformations.map
val readingBooks = MediatorLiveData<List<Book>>()
val completeBooks = MediatorLiveData<List<Book>>()
val layasideBooks = MediatorLiveData<List<Book>>()
init {
readingBooks.addSource(allBooks) { books ->
readingBooks.value = books.filter { it.status == BookStatus.READING }
}
completeBooks.addSource(allBooks) { books ->
completeBooks.value = books.filter { it.status == BookStatus.READ }
}
layasideBooks.addSource(allBooks) { books ->
layasideBooks.value = books.filter { it.status == BookStatus.ON_HOLD }
}
// Update book counts
updateBookCounts()
}
private fun updateBookCounts() {
val counts = mutableMapOf("have read" to 0, "lay aside" to 0, "reading" to 0)
allBooks.observeForever { books ->
counts["have read"] = books.count { it.status == BookStatus.READ }
counts["lay aside"] = books.count { it.status == BookStatus.ON_HOLD }
counts["reading"] = books.count { it.status == BookStatus.READING }
bookCounts.value = counts
}
}
// 添加新书籍
fun addBook(bookTitle: String, bookImage: String, author: String, pages: String, status: BookStatus, readPage: String, press: String,startTime: LocalDate) = viewModelScope.launch {
val newBook = Book(
userId = currentUserUID ?: "",//确保不会空,处理未登陆的情况
title = bookTitle,
image = bookImage,
author = author,
pages = pages,
status = status,
readpage = readPage,
press = press,
startTime = startTime // 设置当前日期为开始时间
)
bookRepository.insert(newBook)
}
// 删除书籍
fun deleteBook(book: Book) = viewModelScope.launch {
bookRepository.delete(book)
}
// 更新书籍状态
fun updateBookStatus(book: Book, newStatus: BookStatus) = viewModelScope.launch {
book.status = newStatus
bookRepository.update(book)
}
// 更新已阅读页数
fun updateBookReadPage(book: Book, readPage: String) = viewModelScope.launch {
book.readpage = readPage // 确保属性名称与你的 Book 类一致
bookRepository.update(book)
}
// 获取指定书籍的笔记数量
fun getNoteCountByBookId(bookId: Int): LiveData<Int> {
return noteRepository.getNoteCountByBookId(bookId)
}
}
3. ReadingRecordViewModel
用于管理与阅读记录相关的数据和操作。以下是代码的主要组成部分和功能:
-
构造函数:接收一个 Application 对象,并在初始化块中创建 ReadingRecordRepository 实例。ReadingRecordRepository 用于访问数据库和执行数据库操作。
-
数据库访问:通过 AppDatabase.getDatabase(application) 获取数据库实例,然后通过 ReadingRecordDao() 方法获取 ReadingRecordDao 实例,这是与阅读记录表交互的接口。
-
LiveData 属性:
- readPagesLast7Days:一个 LiveData<List<ReadingRecordDao.DailyReading>> 属性,用于保存最近7天的每天阅读记录。
- readPagesLast15Days:一个 LiveData<List<ReadingRecordDao.DailyReading>> 属性,用于保存最近15天的每天阅读记录。
-
初始化 LiveData:在初始化块中,使用当前日期和过去日期来计算时间范围,并调用 repository 的 getPagesReadPerDay 方法来获取过去7天和过去15天的每天阅读页数。
-
插入阅读记录:insertReadingRecord 函数接收一个 ReadingRecord 对象,然后在协程作用域内调用 repository.insertReadingRecord 方法将新的阅读记录插入数据库。
-
协程处理:使用 viewModelScope.launch 在 ViewModel 的作用域内启动协程,以便在后台线程上执行数据库操作。
app/src/main/java/com/example/BookRecord/ReadingRecordViewModel.kt
package com.example.BookRecord
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.threeten.bp.LocalDate
class ReadingRecordViewModel(application: Application) : AndroidViewModel(application) {
// 获取对应的Repository实例
private val repository: ReadingRecordRepository
// LiveData保存最近7天和最近15天的每天阅读记录
val readPagesLast7Days: LiveData<List<ReadingRecordDao.DailyReading>>
val readPagesLast15Days: LiveData<List<ReadingRecordDao.DailyReading>>
init {
// 初始化repository和LiveData
val appDatabase = AppDatabase.getDatabase(application)
val readingRecordDao = appDatabase.ReadingRecordDao()
repository = ReadingRecordRepository(readingRecordDao)
val today = LocalDate.now()
val eightDaysAgo = today.minusDays(6) // 收集8天数据以计算7天的增量
val sixteenDaysAgo = today.minusDays(14) // 收集16天数据以计算15天的增量
// 获取过去7天和过去15天的每天的阅读页数
readPagesLast7Days = repository.getPagesReadPerDay(eightDaysAgo, today)
readPagesLast15Days = repository.getPagesReadPerDay(sixteenDaysAgo, today)
}
//插入阅读记录的方法
fun insertReadingRecord(readingRecord: ReadingRecord) {
viewModelScope.launch {
repository.insertReadingRecord(readingRecord)
}
}
}
ps
app的最终页面见和该文章同一专栏下的博文:安卓开发:BookRecord一款专为纸质书爱好者设计的阅读追踪应用