1.优化修改名字dialog的复用

2.优化书签dialog的数据观察
3.添加修改书签名称功能
This commit is contained in:
ocean 2025-09-18 11:46:44 +08:00
parent 5fea68bd95
commit 82bf542cbb
9 changed files with 142 additions and 59 deletions

View File

@ -22,6 +22,7 @@ sealed class FileActionEvent {
object NoticeReload : FileActionEvent() object NoticeReload : FileActionEvent()
data class AddBookmark(val success: Boolean) : FileActionEvent() data class AddBookmark(val success: Boolean) : FileActionEvent()
data class UpdateBookmark(val success: Boolean) : FileActionEvent()
data class DeleteBookmark(val success: Boolean) : FileActionEvent() data class DeleteBookmark(val success: Boolean) : FileActionEvent()
data class DeleteAllBookmark(val success: Boolean) : FileActionEvent() data class DeleteAllBookmark(val success: Boolean) : FileActionEvent()
} }

View File

@ -0,0 +1,6 @@
package com.all.pdfreader.pro.app.model
enum class RenameType {
FILE, // 修改文件名
BOOKMARK // 修改书签名
}

View File

@ -3,10 +3,8 @@ package com.all.pdfreader.pro.app.ui.act
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import androidx.core.os.HandlerCompat.postDelayed
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.R

View File

@ -10,7 +10,7 @@ import com.all.pdfreader.pro.app.room.entity.BookmarkEntity
class BookmarkAdapter( class BookmarkAdapter(
private var list: MutableList<BookmarkEntity>, private var list: MutableList<BookmarkEntity>,
private val onItemClick: (BookmarkEntity) -> Unit, private val onItemClick: (BookmarkEntity) -> Unit,
private val onEditClick: (BookmarkEntity) -> Unit, private val onEditClick: (BookmarkEntity, Int) -> Unit,
private val onDeleteClick: (BookmarkEntity, Int) -> Unit, private val onDeleteClick: (BookmarkEntity, Int) -> Unit,
) : RecyclerView.Adapter<BookmarkAdapter.PdfViewHolder>() { ) : RecyclerView.Adapter<BookmarkAdapter.PdfViewHolder>() {
@ -35,7 +35,7 @@ class BookmarkAdapter(
onItemClick(item) onItemClick(item)
} }
holder.binding.editBtn.setOnClickListener { holder.binding.editBtn.setOnClickListener {
onEditClick(item) onEditClick(item, position)
} }
holder.binding.deleteBtn.setOnClickListener { holder.binding.deleteBtn.setOnClickListener {
onDeleteClick(item, position) onDeleteClick(item, position)
@ -51,6 +51,16 @@ class BookmarkAdapter(
notifyDataSetChanged() notifyDataSetChanged()
} }
fun updateItemChanged(updatedBookmark: BookmarkEntity) {
val index = list.indexOfFirst { it.id == updatedBookmark.id }
if (index != -1) {
list = list.toMutableList().apply {
this[index] = updatedBookmark
}
notifyItemChanged(index)
}
}
fun removeItem(position: Int) { fun removeItem(position: Int) {
list.removeAt(position) list.removeAt(position)
notifyItemRemoved(position) notifyItemRemoved(position)

View File

@ -10,6 +10,7 @@ import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.DialogBookmarksBinding import com.all.pdfreader.pro.app.databinding.DialogBookmarksBinding
import com.all.pdfreader.pro.app.model.RenameType
import com.all.pdfreader.pro.app.room.entity.BookmarkEntity import com.all.pdfreader.pro.app.room.entity.BookmarkEntity
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
import com.all.pdfreader.pro.app.ui.adapter.BookmarkAdapter import com.all.pdfreader.pro.app.ui.adapter.BookmarkAdapter
@ -17,10 +18,8 @@ import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class BookmarksDialogFragment( class BookmarksDialogFragment(
private val pdfDocument: PdfDocumentEntity, private val pdfDocument: PdfDocumentEntity, private val onJumpPage: (Int) -> Unit
private val onJumpPage: (Int) -> Unit ) : BottomSheetDialogFragment() {
) :
BottomSheetDialogFragment() {
private lateinit var binding: DialogBookmarksBinding private lateinit var binding: DialogBookmarksBinding
private val viewModel: PdfViewModel by activityViewModels()//为PdfViewActivity的PdfViewModel private val viewModel: PdfViewModel by activityViewModels()//为PdfViewActivity的PdfViewModel
@ -58,24 +57,29 @@ class BookmarksDialogFragment(
showToast(getString(R.string.file_not)) showToast(getString(R.string.file_not))
dismiss() dismiss()
} }
//观察书签数据 viewModel.pdfBookmark.value?.let {
viewModel.pdfBookmark.observe(this) { list ->
Log.d("ocean","BookmarksDialogFragment observe")
list?.let {
bookmarks = it bookmarks = it
initView() initView()
setupOnClick() setupOnClick()
} ?: run {
showToast(getString(R.string.bookmark_loading))
dismiss()
} }
} }
viewModel.getBookmarks(pdfDocument.filePath)
}
private fun initView() { private fun initView() {
adapter = BookmarkAdapter(list = mutableListOf(), onItemClick = { bookmark -> adapter = BookmarkAdapter(list = mutableListOf(), onItemClick = { bookmark ->
onJumpPage(bookmark.pageNumber) onJumpPage(bookmark.pageNumber)
dismiss() dismiss()
}, onEditClick = { bookmark -> }, onEditClick = { bookmark, position ->
RenameDialogFragment(RenameType.BOOKMARK, bookmark, onOkClick = { updatedBookmark ->
bookmarks = bookmarks.map {//更新外部数据
if (it.id == updatedBookmark.id) updatedBookmark else it
}
adapter.updateItemChanged(updatedBookmark)//更新adapter数据
}).show(
parentFragmentManager, "BookmarksDialogFragment"
)
}, onDeleteClick = { bookmark, position -> }, onDeleteClick = { bookmark, position ->
adapter.removeItem(position) adapter.removeItem(position)
bookmarks = bookmarks.filter { it.id != bookmark.id } bookmarks = bookmarks.filter { it.id != bookmark.id }
@ -83,7 +87,11 @@ class BookmarksDialogFragment(
}) })
binding.bookmarksPageRv.layoutManager = LinearLayoutManager(requireContext()) binding.bookmarksPageRv.layoutManager = LinearLayoutManager(requireContext())
binding.bookmarksPageRv.adapter = adapter binding.bookmarksPageRv.adapter = adapter
updateUi()
updateAdapter()
}
private fun updateUi(){
if (bookmarks.isEmpty()) { if (bookmarks.isEmpty()) {
binding.noBookmarksPageLayout.visibility = View.VISIBLE//显示没有书签的提示布局 binding.noBookmarksPageLayout.visibility = View.VISIBLE//显示没有书签的提示布局
binding.bookmarksDisplayLayout.visibility = View.GONE//隐藏显示内容的布局 binding.bookmarksDisplayLayout.visibility = View.GONE//隐藏显示内容的布局
@ -106,8 +114,6 @@ class BookmarksDialogFragment(
binding.addBtn.setBackgroundResource(R.drawable.dr_click_btn_red_bg) binding.addBtn.setBackgroundResource(R.drawable.dr_click_btn_red_bg)
binding.addIv.setImageResource(R.drawable.add_icon_white) binding.addIv.setImageResource(R.drawable.add_icon_white)
} }
updateAdapter()
} }
} }
@ -126,6 +132,7 @@ class BookmarksDialogFragment(
viewModel.deleteAllBookmark(pdfDocument.filePath) viewModel.deleteAllBookmark(pdfDocument.filePath)
adapter.removeAllItems() adapter.removeAllItems()
bookmarks = emptyList() bookmarks = emptyList()
updateUi()
}).show(parentFragmentManager, "DeleteDialogFragment") }).show(parentFragmentManager, "DeleteDialogFragment")
} }
} }
@ -134,7 +141,7 @@ class BookmarksDialogFragment(
val bookmark = BookmarkEntity( val bookmark = BookmarkEntity(
filePath = pdfDocument.filePath, filePath = pdfDocument.filePath,
pageNumber = pdfDocument.lastReadPage, pageNumber = pdfDocument.lastReadPage,
label = getString(R.string.page) + "${pdfDocument.lastReadPage + 1}" label = getString(R.string.page) + " ${pdfDocument.lastReadPage + 1}"
) )
viewModel.addBookMark(bookmark) viewModel.addBookMark(bookmark)
dismiss() dismiss()

View File

@ -10,6 +10,7 @@ import androidx.fragment.app.activityViewModels
import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.DialogListMoreBinding import com.all.pdfreader.pro.app.databinding.DialogListMoreBinding
import com.all.pdfreader.pro.app.model.PrintResult import com.all.pdfreader.pro.app.model.PrintResult
import com.all.pdfreader.pro.app.model.RenameType
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
import com.all.pdfreader.pro.app.util.AppUtils.dpToPx import com.all.pdfreader.pro.app.util.AppUtils.dpToPx
import com.all.pdfreader.pro.app.util.AppUtils.printPdfFile import com.all.pdfreader.pro.app.util.AppUtils.printPdfFile
@ -95,7 +96,10 @@ class ListMoreDialogFragment(val filePath: String) : BottomSheetDialogFragment()
dismiss() dismiss()
} }
binding.renameFileBtn.setOnClickListener { binding.renameFileBtn.setOnClickListener {
RenameDialogFragment().show(parentFragmentManager, "ListMoreDialogFragment") RenameDialogFragment(RenameType.FILE).show(
parentFragmentManager,
"ListMoreDialogFragment"
)
dismiss() dismiss()
} }
binding.deleteFileBtn.setOnClickListener { binding.deleteFileBtn.setOnClickListener {

View File

@ -13,17 +13,21 @@ import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import com.all.pdfreader.pro.app.R import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.DialogRenameFileBinding import com.all.pdfreader.pro.app.databinding.DialogRenameFileBinding
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.model.RenameType
import com.all.pdfreader.pro.app.room.entity.BookmarkEntity
import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard
import com.all.pdfreader.pro.app.util.FileUtils import com.all.pdfreader.pro.app.util.FileUtils
import com.all.pdfreader.pro.app.viewmodel.PdfViewModel import com.all.pdfreader.pro.app.viewmodel.PdfViewModel
import java.io.File import java.io.File
class RenameDialogFragment() : DialogFragment() { class RenameDialogFragment(
private val type: RenameType,
private val bookmark: BookmarkEntity? = null,
private val onOkClick: (BookmarkEntity) -> Unit= {}
) : DialogFragment() {
private lateinit var binding: DialogRenameFileBinding private lateinit var binding: DialogRenameFileBinding
private val viewModel: PdfViewModel by activityViewModels() private val viewModel: PdfViewModel by activityViewModels()
private lateinit var pdfDocument: PdfDocumentEntity
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
@ -47,35 +51,62 @@ class RenameDialogFragment() : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
when (type) {
RenameType.FILE -> {
viewModel.pdfDocument.value?.let { viewModel.pdfDocument.value?.let {
pdfDocument = it initView(it.fileName)
initView() setupOnClick(it.fileName, it.filePath, null)
setupOnClick()
} ?: run { } ?: run {
showToast(getString(R.string.file_not)) showToast(getString(R.string.file_not))
dismiss() dismiss()
} }
} }
private fun initView() { RenameType.BOOKMARK -> {
binding.etName.showKeyboard() bookmark?.let {
binding.etName.setText(FileUtils.removeFileExtension(pdfDocument.fileName)) initView(it.label)
// 保持光标在末尾 setupOnClick(it.label, it.filePath, it)
binding.etName.setSelection(binding.etName.text?.length ?: 0) } ?: run {
showToast(getString(R.string.file_not))
dismiss()
}
}
}
} }
private fun setupOnClick() { private fun initView(name: String) {
binding.etName.showKeyboard()
binding.etName.setText(FileUtils.removeFileExtension(name))
// 保持光标在末尾
binding.etName.setSelection(binding.etName.text?.length ?: 0)
}
private fun setupOnClick(oldName: String, filePath: String, bookmark: BookmarkEntity?) {
binding.tvCancel.setOnClickListener { binding.tvCancel.setOnClickListener {
dismiss() dismiss()
} }
binding.tvConfirm.setOnClickListener { binding.tvConfirm.setOnClickListener {
when (type) {
RenameType.FILE -> {
val text = binding.etName.text.toString() val text = binding.etName.text.toString()
if (validateEnter(text)) { if (validateEnter(text, oldName, filePath)) {
viewModel.renamePdf(pdfDocument.filePath, text) viewModel.renamePdf(filePath, text)
dismiss() dismiss()
} }
} }
RenameType.BOOKMARK -> {
val text = binding.etName.text.toString()
if (validateEnter(text, oldName, filePath)) {
val new = bookmark?.copy(label = text)!!
viewModel.updateBookmark(new)
onOkClick(new)
dismiss()
}
}
}
}
binding.etName.addTextChangedListener(object : TextWatcher { binding.etName.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
@ -86,19 +117,13 @@ class RenameDialogFragment() : DialogFragment() {
}) })
} }
private fun validateEnter(name: String): Boolean { private fun validateEnter(name: String, oldName: String, filePath: String): Boolean {
// 不允许为空 // 不允许为空
if (name.isBlank()) { if (name.isBlank()) {
binding.tilName.error = getString(R.string.name_not_empty) binding.tilName.error = getString(R.string.name_not_empty)
return false return false
} }
// 名字未做修改(因展示的时候去掉了扩展名,则判断的时候也去掉)
if (name == FileUtils.removeFileExtension(pdfDocument.fileName)) {
binding.tilName.error = getString(R.string.name_not_changed)
return false
}
// 含有非法字符 // 含有非法字符
val invalidChars = "[/\\\\:*?\"<>|.]".toRegex() val invalidChars = "[/\\\\:*?\"<>|.]".toRegex()
if (invalidChars.containsMatchIn(name)) { if (invalidChars.containsMatchIn(name)) {
@ -106,18 +131,37 @@ class RenameDialogFragment() : DialogFragment() {
return false return false
} }
// 长度过长 when (type) {
//只有修改文件名称时,才进行以下判断
RenameType.FILE -> {
if (name.length > 255) { if (name.length > 255) {
binding.tilName.error = getString(R.string.name_too_long) binding.tilName.error = getString(R.string.name_too_long)
return false return false
} }
// 名字未做修改(因展示的时候去掉了扩展名,则判断的时候也去掉)
if (name == FileUtils.removeFileExtension(oldName)) {
binding.tilName.error = getString(R.string.name_not_changed)
return false
}
// 与现有文件重名 // 与现有文件重名
val parentDir = File(pdfDocument.filePath).parentFile val parentDir = File(filePath).parentFile
if (parentDir != null && File(parentDir, name).exists()) { if (parentDir != null && File(parentDir, name).exists()) {
binding.tilName.error = getString(R.string.name_already_exists) binding.tilName.error = getString(R.string.name_already_exists)
return false return false
} }
}
RenameType.BOOKMARK -> {
if (name.length > 50) {
binding.tilName.error = getString(R.string.name_too_long)
return false
}
if (name == oldName) {
binding.tilName.error = getString(R.string.name_not_changed)
return false
}
}
}
// 禁止开头/结尾空格 // 禁止开头/结尾空格
if (name != name.trim()) { if (name != name.trim()) {

View File

@ -18,6 +18,7 @@ import com.all.pdfreader.pro.app.util.FileUtils.isPdfEncrypted
import com.all.pdfreader.pro.app.util.PdfMetadataExtractor import com.all.pdfreader.pro.app.util.PdfMetadataExtractor
import com.all.pdfreader.pro.app.util.PdfSecurityUtils import com.all.pdfreader.pro.app.util.PdfSecurityUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
@ -202,7 +203,7 @@ class PdfViewModel : ViewModel() {
fun getBookmarks(filePath: String) { fun getBookmarks(filePath: String) {
viewModelScope.launch { viewModelScope.launch {
Log.d("ocean", "getBookmarks->filePath: $filePath") Log.d("ocean", "getBookmarks->filePath: $filePath")
pdfRepository.getBookmarksByPdf(filePath).collect { bookmarks -> pdfRepository.getBookmarksByPdf(filePath).distinctUntilChanged().collect { bookmarks ->
_pdfBookmark.value = bookmarks _pdfBookmark.value = bookmarks
} }
} }
@ -217,10 +218,20 @@ class PdfViewModel : ViewModel() {
} }
} }
fun updateBookmark(bookmark: BookmarkEntity){
viewModelScope.launch {
val int = pdfRepository.updateBookmark(bookmark)
val success = int > 0
Log.d("ocean", "updateBookmark->success: $success")
_fileActionEvent.postValue(FileActionEvent.UpdateBookmark(success))
}
}
fun deleteBookMark(bookmark: BookmarkEntity) { fun deleteBookMark(bookmark: BookmarkEntity) {
viewModelScope.launch { viewModelScope.launch {
val int = pdfRepository.deleteBookmark(bookmark) val int = pdfRepository.deleteBookmark(bookmark)
val success = int > 0//受影响行数大于0 val success = int > 0//受影响行数大于0
Log.d("ocean", "deleteBookmark->success: $success")
_fileActionEvent.postValue(FileActionEvent.DeleteBookmark(success)) _fileActionEvent.postValue(FileActionEvent.DeleteBookmark(success))
} }
} }
@ -229,6 +240,7 @@ class PdfViewModel : ViewModel() {
viewModelScope.launch { viewModelScope.launch {
val int = pdfRepository.deleteAllBookmark(filePath) val int = pdfRepository.deleteAllBookmark(filePath)
val success = int > 0 val success = int > 0
Log.d("ocean", "deleteAllBookmark->success: $success")
_fileActionEvent.postValue(FileActionEvent.DeleteAllBookmark(success)) _fileActionEvent.postValue(FileActionEvent.DeleteAllBookmark(success))
} }
} }

View File

@ -113,4 +113,5 @@
<string name="bookmark_remove">Bookmark remove</string> <string name="bookmark_remove">Bookmark remove</string>
<string name="delete_bookmarks_title">Delete Bookmarks</string> <string name="delete_bookmarks_title">Delete Bookmarks</string>
<string name="delete_bookmarks_desc">Are you sure you want to delete all Bookmarks?</string> <string name="delete_bookmarks_desc">Are you sure you want to delete all Bookmarks?</string>
<string name="bookmark_loading">Loading bookmarks, please try again later</string>
</resources> </resources>