From f0fb62777497d4e350af967b0bff40d1d9dbfdb8 Mon Sep 17 00:00:00 2001
From: ocean <503259349@qq.com>
Date: Wed, 24 Sep 2025 17:19:35 +0800
Subject: [PATCH] =?UTF-8?q?1.=E6=B7=BB=E5=8A=A0=E6=90=9C=E7=B4=A2=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD=202.=E4=BC=98=E5=8C=96=E4=B8=BB=E8=A6=81adapter?=
=?UTF-8?q?=E7=9A=84=E5=B1=95=E7=A4=BA=EF=BC=8C=E4=BD=BF=E7=94=A8PdfDiffCa?=
=?UTF-8?q?llback=E6=9D=A5=E5=8F=AA=E5=88=B7=E6=96=B0=E6=9C=89=E6=94=B9?=
=?UTF-8?q?=E5=8F=98=E7=9A=84item?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/src/main/AndroidManifest.xml | 6 +
.../pro/app/room/dao/PdfDocumentDao.kt | 2 +-
.../pro/app/room/repository/PdfRepository.kt | 6 +
.../pdfreader/pro/app/ui/act/MainActivity.kt | 2 +-
.../pro/app/ui/act/SearchActivity.kt | 105 +++++++++++++++++
.../pro/app/ui/adapter/PdfAdapter.kt | 66 +++++++----
.../pro/app/ui/fragment/FavoriteFrag.kt | 2 +-
.../pdfreader/pro/app/ui/fragment/HomeFrag.kt | 2 +-
.../pro/app/ui/fragment/RecentlyFrag.kt | 2 +-
.../all/pdfreader/pro/app/util/AppUtils.kt | 24 ++++
.../pdfreader/pro/app/util/PdfDiffCallback.kt | 20 ++++
.../main/res/layout/activity_search_pdd.xml | 107 ++++++++++++++++++
app/src/main/res/values/strings.xml | 1 +
13 files changed, 320 insertions(+), 25 deletions(-)
create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/ui/act/SearchActivity.kt
create mode 100644 app/src/main/java/com/all/pdfreader/pro/app/util/PdfDiffCallback.kt
create mode 100644 app/src/main/res/layout/activity_search_pdd.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index afd2cb2..15231ad 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -79,6 +79,12 @@
android:label="@string/app_name"
android:screenOrientation="portrait" />
+
+
>
- @Query("SELECT * FROM pdf_documents WHERE fileName LIKE '%' || :query || '%' OR metadataTitle LIKE '%' || :query || '%'")
+ @Query("""SELECT * FROM pdf_documents WHERE :query != '' AND TRIM(:query) != '' AND LOWER(fileName) LIKE '%' || LOWER(:query) || '%' """)
fun searchDocuments(query: String): Flow>
@Query("SELECT * FROM pdf_documents ORDER BY lastOpenedTime DESC")
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/room/repository/PdfRepository.kt b/app/src/main/java/com/all/pdfreader/pro/app/room/repository/PdfRepository.kt
index 8589921..58c90ba 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/room/repository/PdfRepository.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/room/repository/PdfRepository.kt
@@ -39,6 +39,12 @@ class PdfRepository private constructor(context: Context) {
pdfDao.getRecentlyOpenedDocuments()
fun getFavoriteDocuments(): Flow> = pdfDao.getFavoriteDocuments()
+
+ /**
+ * 1.用户输入 " " → 不会返回任何数据
+ * 2.用户输入 " my file " → 会去掉两边空格,正确匹配包含 my file 的文件
+ * 3.用户输入 "my" → 正常模糊匹配
+ */
fun searchDocuments(query: String): Flow> =
pdfDao.searchDocuments(query)
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt
index e8b5b63..69aad5c 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt
@@ -161,7 +161,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
}
binding.searchBtn.setClickWithAnimation {
-
+ startActivity(SearchActivity.createIntent(this))
}
binding.sortingBtn.setClickWithAnimation {
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SearchActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SearchActivity.kt
new file mode 100644
index 0000000..7fc7607
--- /dev/null
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/SearchActivity.kt
@@ -0,0 +1,105 @@
+package com.all.pdfreader.pro.app.ui.act
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.all.pdfreader.pro.app.R
+import com.all.pdfreader.pro.app.databinding.ActivitySearchPddBinding
+import com.all.pdfreader.pro.app.ui.adapter.PdfAdapter
+import com.all.pdfreader.pro.app.ui.dialog.ListMoreDialogFragment
+import com.all.pdfreader.pro.app.util.AppUtils.showKeyboard
+import com.gyf.immersionbar.ImmersionBar
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+class SearchActivity : BaseActivity() {
+ override val TAG: String = "SearchActivity"
+
+ companion object {
+ const val FRAG_TAG = "SearchActivity"
+ fun createIntent(context: Context): Intent {
+ return Intent(context, SearchActivity::class.java)
+ }
+ }
+
+ private lateinit var binding: ActivitySearchPddBinding
+ private lateinit var adapter: PdfAdapter
+ private val pdfRepository = getRepository()
+ private var searchJob: Job? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivitySearchPddBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
+ .navigationBarColor(R.color.bg_color).init()
+ initView()
+ setupClick()
+ }
+
+ private fun setupClick() {
+ binding.backBtn.setOnClickListener {
+ finish()
+ }
+ binding.searchEdit.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
+ override fun afterTextChanged(s: Editable?) {
+ val query = s?.toString().orEmpty()
+ binding.deleteIv.visibility =
+ if (query.isEmpty()) View.GONE else View.VISIBLE
+ binding.searchIv.visibility =
+ if (query.isEmpty()) View.VISIBLE else View.GONE
+ // 取消之前的任务,防止重复 collect
+ searchJob?.cancel()
+ searchJob = lifecycleScope.launch {
+ delay(150)//防止用户飞快打字
+ if (query.isEmpty()) {
+ adapter.updateData(emptyList())
+ binding.noFilesLayout.visibility = View.VISIBLE
+ return@launch
+ }
+ pdfRepository.searchDocuments(query).collectLatest { list ->
+ if (list.isNotEmpty()) {
+ adapter.updateData(list)
+ adapter.highlightItems(query.trim()) // payload 高亮
+ binding.noFilesLayout.visibility = View.GONE
+ } else {
+ adapter.updateData(emptyList())
+ binding.noFilesLayout.visibility = View.VISIBLE
+ }
+ }
+ }
+ }
+ })
+ binding.deleteIv.setOnClickListener {
+ binding.searchEdit.apply {
+ setText("")
+ clearComposingText()
+ setSelection(0)
+ }
+ }
+ }
+
+ private fun initView() {
+ binding.searchEdit.showKeyboard()
+ adapter = PdfAdapter(onItemClick = { pdf ->
+ val intent = PdfViewActivity.createIntent(this, pdf.filePath)
+ startActivity(intent)
+ }, onMoreClick = { pdf ->
+ ListMoreDialogFragment(pdf.filePath).show(supportFragmentManager, FRAG_TAG)
+ })
+
+ binding.recyclerView.layoutManager = LinearLayoutManager(this)
+ binding.recyclerView.adapter = adapter
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfAdapter.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfAdapter.kt
index c62ad0f..7137378 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfAdapter.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfAdapter.kt
@@ -4,62 +4,88 @@ import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
+import com.all.pdfreader.pro.app.R
import com.all.pdfreader.pro.app.databinding.AdapterPdfItemBinding
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.toHighlightedSpannable
import com.all.pdfreader.pro.app.util.FileUtils.toFormatFileSize
import com.all.pdfreader.pro.app.util.FileUtils.toSlashDate
+import com.all.pdfreader.pro.app.util.PdfDiffCallback
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
class PdfAdapter(
- private var pdfList: MutableList,
private val onItemClick: (PdfDocumentEntity) -> Unit,
private val onMoreClick: (PdfDocumentEntity) -> Unit
-) : RecyclerView.Adapter() {
+) : ListAdapter(PdfDiffCallback()) {
inner class PdfViewHolder(val binding: AdapterPdfItemBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PdfViewHolder {
- val binding =
- AdapterPdfItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ val binding = AdapterPdfItemBinding.inflate(
+ LayoutInflater.from(parent.context), parent, false
+ )
return PdfViewHolder(binding)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: PdfViewHolder, position: Int) {
- val item = pdfList[position]
- holder.binding.tvFileName.text = item.fileName
+ bindItem(holder, getItem(position), null)
+ }
+
+ override fun onBindViewHolder(holder: PdfViewHolder, position: Int, payloads: MutableList) {
+ if (payloads.isNotEmpty()) {
+ val payload = payloads[0] as? String
+ bindItem(holder, getItem(position), payload)
+ } else {
+ super.onBindViewHolder(holder, position, payloads)
+ }
+ }
+
+ private fun bindItem(holder: PdfViewHolder, item: PdfDocumentEntity, highlightKeyword: String?) {
+ val context = holder.binding.root.context
+
+ // 文件名高亮,如果 highlightKeyword 为 null 或空字符串,就显示普通文本
+ holder.binding.tvFileName.text = if (highlightKeyword.isNullOrBlank()) {
+ item.fileName
+ } else {
+ item.fileName.toHighlightedSpannable(highlightKeyword, holder.binding.root.context.getColor(R.color.icon_sel_on_color))
+ }
+
holder.binding.tvFileSize.text = item.fileSize.toFormatFileSize()
holder.binding.tvFileDate.text = item.lastModified.toSlashDate()
+
if (item.isPassword) {
holder.binding.lockLayout.visibility = View.VISIBLE
holder.binding.tvFileImg.visibility = View.GONE
} else {
holder.binding.lockLayout.visibility = View.GONE
holder.binding.tvFileImg.visibility = View.VISIBLE
- Glide.with(holder.binding.root).load(item.thumbnailPath)
- .transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context)))
+ Glide.with(context)
+ .load(item.thumbnailPath)
+ .transform(CenterCrop(), RoundedCorners(8.dpToPx(context)))
.into(holder.binding.tvFileImg)
}
- holder.binding.root.setOnClickListener {
- onItemClick(item)
- }
- holder.binding.moreBtn.setOnClickListener {
- onMoreClick(item)
- }
+ holder.binding.root.setOnClickListener { onItemClick(item) }
+ holder.binding.moreBtn.setOnClickListener { onMoreClick(item) }
}
- override fun getItemCount(): Int = pdfList.size
-
- @SuppressLint("NotifyDataSetChanged")
fun updateData(newList: List) {
- pdfList.clear()
- pdfList.addAll(newList)
- notifyDataSetChanged()
+ submitList(newList.toList())
+ }
+
+ /**
+ * 搜索场景使用:只刷新高亮,不刷新整个列表
+ */
+ fun highlightItems(keyword: String) {
+ for (i in 0 until itemCount) {
+ notifyItemChanged(i, keyword)
+ }
}
}
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/FavoriteFrag.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/FavoriteFrag.kt
index d1cb4d9..c07e28a 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/FavoriteFrag.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/FavoriteFrag.kt
@@ -40,7 +40,7 @@ class FavoriteFrag : BaseFrag() {
}
private fun initView() {
- adapter = PdfAdapter(pdfList = mutableListOf(), onItemClick = { pdf ->
+ adapter = PdfAdapter(onItemClick = { pdf ->
val intent = PdfViewActivity.createIntent(requireContext(), pdf.filePath)
startActivity(intent)
}, onMoreClick = { pdf ->
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt
index 7f1145c..5a6fa34 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt
@@ -42,7 +42,7 @@ class HomeFrag : BaseFrag(), MainActivity.SortableFragment {
}
private fun initView() {
- adapter = PdfAdapter(pdfList = mutableListOf(), onItemClick = { pdf ->
+ adapter = PdfAdapter(onItemClick = { pdf ->
val intent = PdfViewActivity.createIntent(requireContext(), pdf.filePath)
startActivity(intent)
}, onMoreClick = { pdf ->
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/RecentlyFrag.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/RecentlyFrag.kt
index 07a03b1..52dc1da 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/RecentlyFrag.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/RecentlyFrag.kt
@@ -39,7 +39,7 @@ class RecentlyFrag : BaseFrag() {
}
private fun initView() {
- adapter = PdfAdapter(pdfList = mutableListOf(), onItemClick = { pdf ->
+ adapter = PdfAdapter(onItemClick = { pdf ->
val intent = PdfViewActivity.createIntent(requireContext(), pdf.filePath)
startActivity(intent)
}, onMoreClick = { pdf ->
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/AppUtils.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/AppUtils.kt
index 28abf24..29605a2 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/util/AppUtils.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/util/AppUtils.kt
@@ -9,6 +9,9 @@ import android.net.Uri
import android.os.ParcelFileDescriptor
import android.print.PrintAttributes
import android.print.PrintManager
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.style.ForegroundColorSpan
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
@@ -28,6 +31,7 @@ import com.tom_roush.pdfbox.pdmodel.common.PDPageLabelRange
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
+import java.util.regex.Pattern
object AppUtils {
@@ -237,4 +241,24 @@ object AppUtils {
null
}
}
+
+ /**
+ * 高亮文字,主要是搜索方面
+ */
+ fun String.toHighlightedSpannable(keyword: String, highlightColor: Int): SpannableString {
+ val spannable = SpannableString(this)
+ if (keyword.isBlank()) return spannable
+
+ val regex = Regex(Pattern.quote(keyword), RegexOption.IGNORE_CASE)
+ regex.findAll(this).forEach { matchResult ->
+ spannable.setSpan(
+ ForegroundColorSpan(highlightColor),
+ matchResult.range.first,
+ matchResult.range.last + 1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
+ )
+ }
+ return spannable
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/PdfDiffCallback.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfDiffCallback.kt
new file mode 100644
index 0000000..d289d96
--- /dev/null
+++ b/app/src/main/java/com/all/pdfreader/pro/app/util/PdfDiffCallback.kt
@@ -0,0 +1,20 @@
+package com.all.pdfreader.pro.app.util
+
+import androidx.recyclerview.widget.DiffUtil
+import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
+
+/**
+ * 用于PdfDocumentEntity数据类的PdfDiff
+ */
+class PdfDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: PdfDocumentEntity, newItem: PdfDocumentEntity): Boolean {
+ return oldItem.filePath == newItem.filePath
+ }
+
+ override fun areContentsTheSame(
+ oldItem: PdfDocumentEntity,
+ newItem: PdfDocumentEntity
+ ): Boolean {
+ return oldItem == newItem
+ }
+}
diff --git a/app/src/main/res/layout/activity_search_pdd.xml b/app/src/main/res/layout/activity_search_pdd.xml
new file mode 100644
index 0000000..0e8db6d
--- /dev/null
+++ b/app/src/main/res/layout/activity_search_pdd.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4ffd7fa..fad71ba 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -135,4 +135,5 @@
Your file has been successfully created
Please select at least one page
Splitting…
+ Search……
\ No newline at end of file