排序dialog

This commit is contained in:
ocean 2025-09-03 10:43:14 +08:00
parent fa82f2eb96
commit c642e3322e
12 changed files with 527 additions and 47 deletions

View File

@ -45,6 +45,7 @@ dependencies {
implementation(libs.androidx.swiperefreshlayout)
implementation(libs.androidx.recyclerview)
implementation(libs.protolite.well.known.types)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

View File

@ -0,0 +1,46 @@
package com.all.pdfreader.pro.app.model
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
enum class SortField {
NAME, DATE, SIZE
}
enum class SortDirection {
ASC, DESC
}
data class SortConfig(
val field: SortField,
val direction: SortDirection
) {
fun toPreferenceString(): String = "${field.name.lowercase()}_${direction.name.lowercase()}"
companion object {
fun fromPreferenceString(value: String): SortConfig {
val parts = value.split("_")
val field = SortField.valueOf(parts[0].uppercase())
val direction = SortDirection.valueOf(parts[1].uppercase())
return SortConfig(field, direction)
}
fun default(): SortConfig = SortConfig(SortField.DATE, SortDirection.DESC)
}
fun applySort(documents: List<PdfDocumentEntity>): List<PdfDocumentEntity> {
return when (field) {
SortField.NAME -> when (direction) {
SortDirection.ASC -> documents.sortedBy { it.fileName.lowercase() }
SortDirection.DESC -> documents.sortedByDescending { it.fileName.lowercase() }
}
SortField.DATE -> when (direction) {
SortDirection.ASC -> documents.sortedBy { it.lastModified }
SortDirection.DESC -> documents.sortedByDescending { it.lastModified }
}
SortField.SIZE -> when (direction) {
SortDirection.ASC -> documents.sortedBy { it.fileSize }
SortDirection.DESC -> documents.sortedByDescending { it.fileSize }
}
}
}
}

View File

@ -1,6 +1,7 @@
package com.all.pdfreader.pro.app.sp
import android.content.Context
import com.all.pdfreader.pro.app.model.SortConfig
import com.all.pdfreader.pro.app.sp.store.Store
import com.all.pdfreader.pro.app.sp.store.asStoreProvider
@ -14,9 +15,9 @@ class AppStore(context: Context) {
key = PERMISSIONS_DIALOG_PROMPT, defaultValue = false
)
// 文档排序方式name_asc, name_desc, date_asc, date_desc, size_asc, size_desc
// 文档排序配置
var documentSortType: String by store.string(
key = DOCUMENT_SORT_TYPE, defaultValue = "date_desc"
key = DOCUMENT_SORT_TYPE, defaultValue = SortConfig.default().toPreferenceString()
)
companion object {

View File

@ -14,6 +14,10 @@ import com.all.pdfreader.pro.app.ui.fragment.FavoriteFrag
import com.all.pdfreader.pro.app.ui.fragment.HomeFrag
import com.all.pdfreader.pro.app.ui.fragment.RecentlyFrag
import com.all.pdfreader.pro.app.ui.fragment.ToolsFrag
import com.all.pdfreader.pro.app.model.SortConfig
import com.all.pdfreader.pro.app.model.SortField
import com.all.pdfreader.pro.app.model.SortDirection
import com.all.pdfreader.pro.app.ui.dialog.SortDialogFragment
import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation
import com.all.pdfreader.pro.app.util.FileChangeObserver
import com.all.pdfreader.pro.app.util.FileUtils
@ -48,7 +52,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true)
.navigationBarColor(R.color.white).init()
.navigationBarColor(R.color.black).init()
setupFragments()
setupNavigation()
@ -108,7 +112,10 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
}
binding.sortingBtn.setClickWithAnimation {
showSortDialog()
SortDialogFragment(onOkClick = { it ->
// 通知当前fragment更新排序
(activeFragment as? SortableFragment)?.onSortTypeChanged(it)
}).show(supportFragmentManager, TAG)
}
}
@ -119,36 +126,6 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback
updateSelectedNav(target)
}
private fun showSortDialog() {
val sortOptions = arrayOf(
"名称升序", "名称降序",
"日期升序", "日期降序",
"大小升序", "大小降序"
)
val sortValues = arrayOf(
"name_asc", "name_desc",
"date_asc", "date_desc",
"size_asc", "size_desc"
)
val currentSort = appStore.documentSortType
val checkedItem = sortValues.indexOf(currentSort)
android.app.AlertDialog.Builder(this)
.setTitle("选择排序方式")
.setSingleChoiceItems(sortOptions, checkedItem) { dialog, which ->
val newSortType = sortValues[which]
appStore.documentSortType = newSortType
// 通知当前fragment更新排序
(activeFragment as? SortableFragment)?.onSortTypeChanged(newSortType)
dialog.dismiss()
}
.setNegativeButton("取消", null)
.show()
}
interface SortableFragment {
fun onSortTypeChanged(sortType: String)
}

View File

@ -86,15 +86,15 @@ class PermissionDialogFragment : DialogFragment() {
private fun showPermissionDeniedDialog() {
AlertDialog.Builder(requireContext())
.setTitle("权限被拒绝")
.setMessage("无法访问PDF文件。请在设置中授予存储权限")
.setPositiveButton("前往设置") { _, _ ->
.setTitle(getString(R.string.permission_denied))
.setMessage(getString(R.string.pd_content_notice))
.setPositiveButton(getString(R.string.go)) { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = "package:${requireContext().packageName}".toUri()
startActivity(intent)
dismiss()
}
.setNegativeButton("退出") { _, _ ->
.setNegativeButton(getString(R.string.exit)) { _, _ ->
dismiss()
}
.setCancelable(false)

View File

@ -0,0 +1,182 @@
package com.all.pdfreader.pro.app.ui.dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.all.pdfreader.pro.app.databinding.DialogPermissionBinding
import com.all.pdfreader.pro.app.databinding.DialogSortBinding
import com.all.pdfreader.pro.app.model.SortConfig
import com.all.pdfreader.pro.app.model.SortDirection
import com.all.pdfreader.pro.app.model.SortField
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
import com.all.pdfreader.pro.app.sp.AppStore
import com.all.pdfreader.pro.app.ui.act.MainActivity.SortableFragment
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class SortDialogFragment(
private val onOkClick: (String) -> Unit
) : BottomSheetDialogFragment() {
private lateinit var binding: DialogSortBinding
private lateinit var sortConfig: SortConfig
private lateinit var selectedField: SortField
private lateinit var selectedDirection: SortDirection
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
binding = DialogSortBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val appStore = AppStore(requireActivity())
isCancelable = false
sortConfig = SortConfig.fromPreferenceString(appStore.documentSortType)
selectedField = sortConfig.field
selectedDirection = sortConfig.direction
setAllAlphaUnselected()
when (sortConfig.field) {
SortField.DATE -> {
setViewsAlpha(1f, binding.createdDateIv, binding.createdDateTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.createdDateGou)
}
SortField.NAME -> {
setViewsAlpha(1f, binding.fileNameIv, binding.fileNameTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.fileNameGou)
}
SortField.SIZE -> {
setViewsAlpha(1f, binding.fileSizeIv, binding.fileSizeTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.fileSizeGou)
}
}
when (sortConfig.direction) {
SortDirection.ASC -> {
setViewsAlpha(1f, binding.ascendingIv, binding.ascendingTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.ascendingGou)
}
SortDirection.DESC -> {
setViewsAlpha(1f, binding.descendingIv, binding.descendingTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.descendingGou)
}
}
binding.createdDateBtn.setOnClickListener {
setAllAlphaTop()
setViewsAlpha(1f, binding.createdDateIv, binding.createdDateTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.createdDateGou)
selectedField = SortField.DATE
}
binding.fileNameBtn.setOnClickListener {
setAllAlphaTop()
setViewsAlpha(1f, binding.fileNameIv, binding.fileNameTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.fileNameGou)
selectedField = SortField.NAME
}
binding.fileSizeBtn.setOnClickListener {
setAllAlphaTop()
setViewsAlpha(1f, binding.fileSizeIv, binding.fileSizeTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.fileSizeGou)
selectedField = SortField.SIZE
}
binding.ascendingBtn.setOnClickListener {
setAllAlphaBoo()
setViewsAlpha(1f, binding.ascendingIv, binding.ascendingTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.ascendingGou)
selectedDirection = SortDirection.ASC
}
binding.descendingBtn.setOnClickListener {
setAllAlphaBoo()
setViewsAlpha(1f, binding.descendingIv, binding.descendingTv)
setViewsGoneIsVISIBLE(View.VISIBLE, binding.descendingGou)
selectedDirection = SortDirection.DESC
}
binding.cancelBtn.setOnClickListener {
dismiss()
}
binding.okBtn.setOnClickListener {
val newConfig = SortConfig(
field = selectedField, direction = selectedDirection
)
appStore.documentSortType = newConfig.toPreferenceString()
onOkClick(appStore.documentSortType)
dismiss()
}
}
private fun setAllAlphaTop() {
setViewsAlpha(
0.5f,
binding.createdDateIv,
binding.createdDateTv,
binding.fileNameIv,
binding.fileNameTv,
binding.fileSizeIv,
binding.fileSizeTv,
)
setViewsGoneIsVISIBLE(
View.GONE,
binding.createdDateGou,
binding.fileNameGou,
binding.fileSizeGou,
)
}
private fun setAllAlphaBoo() {
setViewsAlpha(
0.5f,
binding.ascendingIv,
binding.ascendingTv,
binding.descendingIv,
binding.descendingTv
)
setViewsGoneIsVISIBLE(
View.GONE,
binding.ascendingGou,
binding.descendingGou
)
}
private fun setAllAlphaUnselected() {
setViewsAlpha(
0.5f,
binding.createdDateIv,
binding.createdDateTv,
binding.fileNameIv,
binding.fileNameTv,
binding.fileSizeIv,
binding.fileSizeTv,
binding.ascendingIv,
binding.ascendingTv,
binding.descendingIv,
binding.descendingTv
)
setViewsGoneIsVISIBLE(
View.GONE,
binding.createdDateGou,
binding.fileNameGou,
binding.fileSizeGou,
binding.ascendingGou,
binding.descendingGou
)
}
private fun setViewsAlpha(alpha: Float, vararg views: View) {
views.forEach { it.alpha = alpha }
}
private fun setViewsGoneIsVISIBLE(int: Int, vararg views: View) {
views.forEach { it.visibility = int }
}
}

View File

@ -10,6 +10,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.all.pdfreader.pro.app.databinding.FragmentHomeBinding
import com.all.pdfreader.pro.app.model.SortConfig
import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity
import com.all.pdfreader.pro.app.room.repository.PdfRepository
import com.all.pdfreader.pro.app.ui.act.MainActivity
@ -72,15 +73,8 @@ class HomeFrag : BaseFrag(), MainActivity.SortableFragment {
}
private fun sortDocuments(documents: List<PdfDocumentEntity>): List<PdfDocumentEntity> {
return when (appStore.documentSortType) {
"name_asc" -> documents.sortedBy { it.fileName.lowercase() }
"name_desc" -> documents.sortedByDescending { it.fileName.lowercase() }
"date_asc" -> documents.sortedBy { it.lastModified }
"date_desc" -> documents.sortedByDescending { it.lastModified }
"size_asc" -> documents.sortedBy { it.fileSize }
"size_desc" -> documents.sortedByDescending { it.fileSize }
else -> documents
}
val sortConfig = SortConfig.fromPreferenceString(appStore.documentSortType)
return sortConfig.applySort(documents)
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下状态 -->
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#80E6E6E6"/> <!-- 按下颜色:深一点 -->
<corners android:radius="12dp"/>
</shape>
</item>
<!-- 默认状态 -->
<item>
<shape android:shape="rectangle">
<solid android:color="#E6E6E6"/> <!-- 默认颜色 -->
<corners android:radius="12dp"/>
</shape>
</item>
</selector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M384,768c-12.8,0 -21.3,-4.3 -29.9,-12.8l-213.3,-213.3c-17.1,-17.1 -17.1,-42.7 0,-59.7s42.7,-17.1 59.7,0L384,665.6 823.5,226.1c17.1,-17.1 42.7,-17.1 59.7,0s17.1,42.7 0,59.7l-469.3,469.3c-8.5,8.5 -17.1,12.8 -29.9,12.8z"
android:fillColor="#4E78BA"/>
</vector>

View File

@ -0,0 +1,237 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/dr_rounded_corner_12_bg_white"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/sort_by"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/createdDateBtn"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/createdDateIv"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/createdDateTv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:text="@string/created_date"
android:textColor="@color/black"
android:textSize="16sp" />
<ImageView
android:id="@+id/createdDateGou"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/gou" />
</LinearLayout>
<LinearLayout
android:id="@+id/fileNameBtn"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/fileNameIv"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/fileNameTv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:text="@string/file_name"
android:textColor="@color/black"
android:textSize="16sp" />
<ImageView
android:id="@+id/fileNameGou"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/gou" />
</LinearLayout>
<LinearLayout
android:id="@+id/fileSizeBtn"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/fileSizeIv"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/fileSizeTv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:text="@string/file_size"
android:textColor="@color/black"
android:textSize="16sp" />
<ImageView
android:id="@+id/fileSizeGou"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/gou" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/line_color" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ascendingBtn"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/ascendingIv"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/ascendingTv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:text="@string/ascending"
android:textColor="@color/black"
android:textSize="16sp" />
<ImageView
android:id="@+id/ascendingGou"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/gou" />
</LinearLayout>
<LinearLayout
android:id="@+id/descendingBtn"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/descendingIv"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/descendingTv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:text="@string/descending"
android:textColor="@color/black"
android:textSize="16sp" />
<ImageView
android:id="@+id/descendingGou"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/gou" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<LinearLayout
android:id="@+id/cancelBtn"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:background="@drawable/dr_cancel_btn_bg"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:textColor="@color/black"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:id="@+id/okBtn"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:background="@drawable/dr_click_btn_bg"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok"
android:textColor="@color/white"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -10,6 +10,17 @@
<string name="permission_required_desc_2">Allow access to manage all files</string>
<string name="allow_access">Allow Access</string>
<string name="go">Go</string>
<string name="exit">Exit</string>
<string name="notice">Notice</string>
<string name="permission_notice">Permission is required to access files</string>
<string name="cancel">Cancel</string>
<string name="ok">OK</string>
<string name="sort_by">Sort by</string>
<string name="created_date">Created Date</string>
<string name="file_name">File Name</string>
<string name="file_size">File Size</string>
<string name="ascending">Ascending</string>
<string name="descending">Descending</string>
<string name="permission_denied">权限被拒绝</string>
<string name="pd_content_notice">Unable to access PDF file, please grant storage permission in settings</string>
</resources>

View File

@ -14,6 +14,7 @@ room_version = "2.7.2"
swiperefreshlayout = "1.1.0"
recyclerview = "1.4.0"
protoliteWellKnownTypes = "18.0.1"
material = "1.12.0"
[libraries]
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
@ -30,6 +31,7 @@ immersionbar-ktx = { group = "com.geyifeng.immersionbar", name = "immersionbar-k
androidx-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" }
androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" }
protolite-well-known-types = { group = "com.google.firebase", name = "protolite-well-known-types", version.ref = "protoliteWellKnownTypes" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }