添加书签快速定位至目录的功能

This commit is contained in:
HapeLee
2025-09-18 19:54:40 +08:00
parent 76f125a15e
commit f123ef2eaa
6 changed files with 103 additions and 29 deletions
@@ -41,11 +41,17 @@ class BookmarkAdapter(context: Context, val callback: Callback) :
callback.onClick(bookmark) callback.onClick(bookmark)
} }
} }
binding.btnLocate.onClick {
getItem(holder.layoutPosition)?.let { bookmark ->
callback.onLocate(bookmark)
}
}
} }
interface Callback { interface Callback {
fun onClick(bookmark: Bookmark) fun onClick(bookmark: Bookmark)
fun onLongClick(bookmark: Bookmark, pos: Int) fun onLongClick(bookmark: Bookmark, pos: Int)
fun onLocate(bookmark: Bookmark)
} }
} }
@@ -12,10 +12,8 @@ import io.legato.kazusa.constant.AppLog
import io.legato.kazusa.data.appDb import io.legato.kazusa.data.appDb
import io.legato.kazusa.data.entities.Bookmark import io.legato.kazusa.data.entities.Bookmark
import io.legato.kazusa.databinding.FragmentBookmarkBinding import io.legato.kazusa.databinding.FragmentBookmarkBinding
//import io.legado.app.lib.theme.primaryColor
import io.legato.kazusa.ui.book.bookmark.BookmarkDialog import io.legato.kazusa.ui.book.bookmark.BookmarkDialog
import io.legato.kazusa.ui.widget.recycler.UpLinearLayoutManager import io.legato.kazusa.ui.widget.recycler.UpLinearLayoutManager
import io.legato.kazusa.ui.widget.recycler.VerticalDivider
import io.legato.kazusa.utils.applyNavigationBarPadding import io.legato.kazusa.utils.applyNavigationBarPadding
import io.legato.kazusa.utils.showDialogFragment import io.legato.kazusa.utils.showDialogFragment
import io.legato.kazusa.utils.viewbindingdelegate.viewBinding import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
@@ -49,10 +47,8 @@ class BookmarkFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_bookmark
private fun initRecyclerView() { private fun initRecyclerView() {
//binding.recyclerView.setEdgeEffectColor(primaryColor) //binding.recyclerView.setEdgeEffectColor(primaryColor)
binding.recyclerView.layoutManager = mLayoutManager binding.recyclerView.layoutManager = mLayoutManager
binding.recyclerView.addItemDecoration(VerticalDivider(requireContext()))
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
binding.recyclerView.applyNavigationBarPadding() binding.recyclerView.applyNavigationBarPadding()
binding.tvEmptyMsg.visible(false) binding.tvEmptyMsg.visible(false)
} }
@@ -96,4 +92,8 @@ class BookmarkFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_bookmark
showDialogFragment(BookmarkDialog(bookmark, pos)) showDialogFragment(BookmarkDialog(bookmark, pos))
} }
override fun onLocate(bookmark: Bookmark) {
(activity as? TocActivity)?.locateToChapter(bookmark.chapterIndex)
}
} }
@@ -212,7 +212,7 @@ class ChapterListAdapter(
if (selectedIndices.contains(item.index)) { if (selectedIndices.contains(item.index)) {
ivVolume.gone() ivVolume.gone()
tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurfaceBright)) tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurfaceContainerHighest))
} else { } else {
if (item.isVolume) { if (item.isVolume) {
ivVolume.visible() ivVolume.visible()
@@ -1,10 +1,14 @@
package io.legato.kazusa.ui.book.toc package io.legato.kazusa.ui.book.toc
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
import android.content.Intent import android.content.Intent
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Rect import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.os.Bundle
import android.transition.TransitionManager import android.transition.TransitionManager
import android.view.View import android.view.View
@@ -260,6 +264,35 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
} }
} }
fun scrollToChapter(chapterIndex: Int) {
val pos = adapter.getItems().indexOfFirst { it.index == chapterIndex }
if (pos != -1) {
mLayoutManager.scrollToPositionWithOffset(pos, 0)
binding.recyclerView.post {
val holder = binding.recyclerView.findViewHolderForAdapterPosition(pos)
holder?.itemView?.let { view ->
flashHighlight(view)
}
}
}
}
private fun flashHighlight(view: View) {
val highlightColor = context?.themeColor(com.google.android.material.R.attr.colorSurfaceContainerHighest)
val originalColor = (view.background as? ColorDrawable)?.color ?: Color.TRANSPARENT
val anim = ValueAnimator.ofObject(ArgbEvaluator(), originalColor, highlightColor, originalColor).apply {
duration = 500
repeatCount = 1
repeatMode = ValueAnimator.RESTART
addUpdateListener { animator ->
val color = animator.animatedValue as Int
view.setBackgroundColor(color)
}
}
anim.start()
}
override fun onSelectionModeChanged(enabled: Boolean) { override fun onSelectionModeChanged(enabled: Boolean) {
TransitionManager.beginDelayedTransition(binding.coordinatorLayout, MaterialFadeThrough()) TransitionManager.beginDelayedTransition(binding.coordinatorLayout, MaterialFadeThrough())
@@ -210,6 +210,15 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
} }
} }
fun locateToChapter(index: Int) {
val tag = "android:switcher:${binding.viewPager.id}:0"
val chapterFragment = supportFragmentManager.findFragmentByTag(tag) as? ChapterListFragment
chapterFragment?.scrollToChapter(index)
binding.viewPager.currentItem = 0
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun showDownloadDialog() { fun showDownloadDialog() {
ReadBook.book?.let { book -> ReadBook.book?.let { book ->
+50 -24
View File
@@ -1,34 +1,60 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingHorizontal="16dp" android:paddingStart="16dp"
android:paddingEnd="10dp"
android:paddingVertical="12dp" android:paddingVertical="12dp"
android:background="?attr/selectableItemBackground"> android:background="?attr/selectableItemBackground">
<TextView <com.google.android.material.button.MaterialButton
android:id="@+id/tv_chapter_name" android:id="@+id/btn_locate"
android:layout_width="match_parent" android:layout_width="40dp"
android:layout_height="wrap_content" android:layout_height="40dp"
android:paddingBottom="2dp" app:iconSize="16dp"
android:textSize="16sp" app:iconGravity="textStart"
android:textStyle="bold" /> app:icon="@drawable/ic_locate"
style="@style/Widget.Material3Expressive.Button.IconButton.Outlined"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView <LinearLayout
android:id="@+id/tv_book_text" android:layout_width="0dp"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_height="wrap_content" android:orientation="vertical"
android:paddingBottom="2dp" android:layout_marginEnd="8dp"
android:textSize="14sp" app:layout_constraintStart_toStartOf="parent"
android:singleLine="true" /> app:layout_constraintEnd_toStartOf="@id/btn_locate"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="@+id/tv_chapter_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="2dp"
android:textSize="16sp"
android:textStyle="bold" />
<TextView <TextView
android:id="@+id/tv_content" android:id="@+id/tv_book_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="?attr/colorSecondary" android:paddingBottom="2dp"
android:textSize="12sp" android:textSize="13sp"
android:singleLine="true" /> android:singleLine="true" />
</LinearLayout> <TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/colorSecondary"
android:textSize="12sp"
android:singleLine="true" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>