From bf514ffd268e8435833aecb4c108551dc54f70fd Mon Sep 17 00:00:00 2001 From: HapeLee <1321903405@qq.com> Date: Thu, 21 Aug 2025 04:58:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/book/bookmark/BookmarkDecoration.kt | 4 +- .../kazusa/ui/book/read/ReadBookActivity.kt | 9 +- .../kazusa/ui/book/toc/BookmarkAdapter.kt | 4 +- .../kazusa/ui/book/toc/BookmarkFragment.kt | 18 +-- .../kazusa/ui/book/toc/ChapterListAdapter.kt | 82 +++++++------ .../kazusa/ui/book/toc/ChapterListFragment.kt | 30 ++--- .../legato/kazusa/ui/book/toc/TocActivity.kt | 5 +- app/src/main/res/drawable/ic_divider.xml | 16 ++- .../main/res/drawable/ic_download_done.xml | 11 ++ .../main/res/layout/activity_chapter_list.xml | 2 +- app/src/main/res/layout/activity_manga.xml | 4 +- app/src/main/res/layout/dialog_bookmark.xml | 42 ++++--- app/src/main/res/layout/dialog_read_aloud.xml | 14 +-- app/src/main/res/layout/fragment_bookmark.xml | 28 +++++ app/src/main/res/layout/fragment_books.xml | 2 +- app/src/main/res/layout/item_chapter_list.xml | 110 +++++++++++------- app/src/main/res/layout/item_search.xml | 1 + app/src/main/res/layout/view_load_more.xml | 2 +- 18 files changed, 230 insertions(+), 154 deletions(-) create mode 100644 app/src/main/res/drawable/ic_download_done.xml diff --git a/app/src/main/java/io/legato/kazusa/ui/book/bookmark/BookmarkDecoration.kt b/app/src/main/java/io/legato/kazusa/ui/book/bookmark/BookmarkDecoration.kt index dc02d49aa..e8c36883d 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/bookmark/BookmarkDecoration.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/bookmark/BookmarkDecoration.kt @@ -5,6 +5,7 @@ import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Rect +import android.graphics.Typeface import android.text.TextPaint import android.view.View import androidx.recyclerview.widget.LinearLayoutManager @@ -28,10 +29,11 @@ class BookmarkDecoration( } private val textPaint = TextPaint().apply { - textSize = 16f.spToPx() + textSize = 14f.spToPx() color = MaterialColors.getColor(context, androidx.appcompat.R.attr.colorPrimary, Color.BLACK) isAntiAlias = true + typeface = Typeface.DEFAULT_BOLD } private val textRect = Rect() diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt index 26c79cda5..8eaf26663 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt @@ -8,6 +8,7 @@ import android.net.Uri import android.os.Bundle import android.os.Looper import android.view.Gravity +import android.view.HapticFeedbackConstants import android.view.InputDevice import android.view.KeyEvent import android.view.Menu @@ -788,21 +789,25 @@ class ReadBookActivity : BaseReadBookActivity(), /** * 更新文字选择开始位置 */ - override fun upSelectedStart(x: Float, y: Float, top: Float) = binding.run { + override fun upSelectedStart(x: Float, y: Float, top: Float): Unit = binding.run { cursorLeft.x = x - cursorLeft.width cursorLeft.y = y cursorLeft.visible(true) textMenuPosition.x = x textMenuPosition.y = top + + root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) } /** * 更新文字选择结束位置 */ - override fun upSelectedEnd(x: Float, y: Float) = binding.run { + override fun upSelectedEnd(x: Float, y: Float): Unit = binding.run { cursorRight.x = x cursorRight.y = y cursorRight.visible(true) + + root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) } /** diff --git a/app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkAdapter.kt b/app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkAdapter.kt index 33a2990b0..d322f7c4a 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkAdapter.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkAdapter.kt @@ -32,12 +32,12 @@ class BookmarkAdapter(context: Context, val callback: Callback) : override fun registerListener(holder: ItemViewHolder, binding: ItemBookmarkBinding) { binding.root.setOnClickListener { getItem(holder.layoutPosition)?.let { bookmark -> - callback.onClick(bookmark) + callback.onLongClick(bookmark, holder.layoutPosition) } } binding.root.onLongClick { getItem(holder.layoutPosition)?.let { bookmark -> - callback.onLongClick(bookmark, holder.layoutPosition) + callback.onClick(bookmark) } } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkFragment.kt index be759664c..fb52396c0 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkFragment.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkFragment.kt @@ -19,6 +19,7 @@ import io.legato.kazusa.ui.widget.recycler.VerticalDivider import io.legato.kazusa.utils.applyNavigationBarPadding import io.legato.kazusa.utils.showDialogFragment import io.legato.kazusa.utils.viewbindingdelegate.viewBinding +import io.legato.kazusa.utils.visible import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.flow.catch @@ -51,24 +52,27 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_bookmark binding.recyclerView.addItemDecoration(VerticalDivider(requireContext())) binding.recyclerView.adapter = adapter binding.recyclerView.applyNavigationBarPadding() + + binding.tvEmptyMsg.visible(false) } override fun upBookmark(searchKey: String?) { val book = viewModel.bookData.value ?: return lifecycleScope.launch { - when { + val flow = when { searchKey.isNullOrBlank() -> appDb.bookmarkDao.flowByBook(book.name, book.author) else -> appDb.bookmarkDao.flowSearch(book.name, book.author, searchKey) - }.catch { + } + + flow.catch { AppLog.put("目录界面获取书签数据失败\n${it.localizedMessage}", it) - }.flowOn(IO).collect { - adapter.setItems(it) + }.flowOn(IO).collect { list -> + adapter.setItems(list) + binding.tvEmptyMsg.visible(list.isEmpty()) var scrollPos = 0 withContext(Dispatchers.Default) { adapter.getItems().forEachIndexed { index, bookmark -> - if (bookmark.chapterIndex >= durChapterIndex) { - return@withContext - } + if (bookmark.chapterIndex >= durChapterIndex) return@withContext scrollPos = index } } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListAdapter.kt b/app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListAdapter.kt index 67d4aaf18..91d34729f 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListAdapter.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListAdapter.kt @@ -14,15 +14,17 @@ import io.legato.kazusa.data.entities.BookChapter import io.legato.kazusa.databinding.ItemChapterListBinding import io.legato.kazusa.help.book.ContentProcessor import io.legato.kazusa.help.config.AppConfig -import io.legato.kazusa.help.coroutine.Coroutine import io.legato.kazusa.lib.theme.ThemeUtils import io.legato.kazusa.utils.gone import io.legato.kazusa.utils.longToastOnUi import io.legato.kazusa.utils.themeColor import io.legato.kazusa.utils.visible import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.ensureActive import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.util.concurrent.ConcurrentHashMap class ChapterListAdapter(context: Context, val callback: Callback) : @@ -30,6 +32,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) : val cacheFileNames = hashSetOf() private val displayTitleMap = ConcurrentHashMap() + private val handler = Handler(Looper.getMainLooper()) override val diffItemCallback: DiffUtil.ItemCallback @@ -58,7 +61,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) : } - private var upDisplayTileJob: Coroutine<*>? = null + private var upDisplayTileJob: Job? = null override fun onCurrentListChanged() { super.onCurrentListChanged() @@ -72,42 +75,31 @@ class ChapterListAdapter(context: Context, val callback: Callback) : fun upDisplayTitles(startIndex: Int) { upDisplayTileJob?.cancel() - upDisplayTileJob = Coroutine.async(callback.scope) { - val book = callback.book ?: return@async + upDisplayTileJob = callback.scope.launch(Dispatchers.Default) { + val book = callback.book ?: return@launch val replaceRules = ContentProcessor.get(book.name, book.origin).getTitleReplaceRules() val useReplace = AppConfig.tocUiUseReplace && book.getUseReplaceRule() val items = getItems() - launch { - for (i in startIndex until items.size) { - val item = items[i] - if (displayTitleMap[item.title] == null) { - ensureActive() - val displayTitle = item.getDisplayTitle(replaceRules, useReplace) - ensureActive() - displayTitleMap[item.title] = displayTitle - handler.post { - notifyItemChanged(i, true) - } - } - } - } - launch { - for (i in startIndex downTo 0) { - val item = items[i] - if (displayTitleMap[item.title] == null) { - ensureActive() - val displayTitle = item.getDisplayTitle(replaceRules, useReplace) - ensureActive() - displayTitleMap[item.title] = displayTitle - handler.post { - notifyItemChanged(i, true) - } + + val indices = (startIndex until items.size) + (startIndex downTo 0) + + for (i in indices) { + val item = items[i] + val key = item.url + if (displayTitleMap[key] == null) { + ensureActive() + val displayTitle = item.getDisplayTitle(replaceRules, useReplace) + displayTitleMap[key] = displayTitle + ensureActive() + withContext(Dispatchers.Main) { + notifyItemChanged(i, true) } } } } } + private fun getDisplayTitle(chapter: BookChapter): String { return displayTitleMap[chapter.title] ?: chapter.title } @@ -128,17 +120,22 @@ class ChapterListAdapter(context: Context, val callback: Callback) : || item.isVolume || cacheFileNames.contains(item.getFileName()) if (payloads.isEmpty()) { - if (isDur) { - tvChapterName.setTextColor(context.themeColor(androidx.appcompat.R.attr.colorPrimary)) - tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurfaceContainer)) - } else { - tvChapterName.setTextColor(context.themeColor(com.google.android.material.R.attr.colorOnSurface)) - tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurface)) - } - tvChapterName.text = getDisplayTitle(item) + if (item.isVolume) { - tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSecondaryContainer)) + ivVolume.visible() + tvChapterName.text = getDisplayTitle(item) + tvChapterName.textSize = 12f + tvChapterName.setTextColor(context.themeColor(com.google.android.material.R.attr.colorTertiary)) } else { + ivVolume.gone() + if (isDur) { + tvChapterName.setTextColor(context.themeColor(androidx.appcompat.R.attr.colorPrimary)) + tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurfaceContainer)) + } else { + tvChapterName.setTextColor(context.themeColor(com.google.android.material.R.attr.colorOnSurface)) + tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurface)) + } + tvChapterName.text = getDisplayTitle(item) tvChapterItem.foreground = ThemeUtils.resolveDrawable(context, android.R.attr.selectableItemBackground) } @@ -165,10 +162,10 @@ class ChapterListAdapter(context: Context, val callback: Callback) : ivLocked.gone() } - upHasCache(binding, isDur, cached) + upHasCache(binding, cached) } else { tvChapterName.text = getDisplayTitle(item) - upHasCache(binding, isDur, cached) + upHasCache(binding, cached) } } } @@ -187,12 +184,11 @@ class ChapterListAdapter(context: Context, val callback: Callback) : } } - private fun upHasCache(binding: ItemChapterListBinding, isDur: Boolean, cached: Boolean) = + private fun upHasCache(binding: ItemChapterListBinding, cached: Boolean) = binding.apply { when { - isDur -> ivChecked.gone() cached -> { - ivChecked.setImageResource(R.drawable.ic_check) + ivChecked.setImageResource(R.drawable.ic_download_done) ivChecked.visible() } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListFragment.kt b/app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListFragment.kt index d09394559..f8540e0bb 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListFragment.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListFragment.kt @@ -1,7 +1,5 @@ package io.legato.kazusa.ui.book.toc -//import io.legado.app.lib.theme.bottomBackground -//import io.legado.app.lib.theme.getPrimaryTextColor import android.annotation.SuppressLint import android.app.Activity.RESULT_OK import android.content.Intent @@ -36,7 +34,6 @@ import io.legato.kazusa.utils.themeColor import io.legato.kazusa.utils.toastOnUi import io.legato.kazusa.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers.Default import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch @@ -79,7 +76,7 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt toastOnUi("开始下载: ${adapter.getItem(position)?.title}") CacheBook.start(requireContext(), book, chapter.index, chapter.index) - VibrationUtils.vibrate(context!!, 100) + VibrationUtils.vibratePattern(requireContext(), longArrayOf(0, 50, 30, 50), -1) adapter.notifyItemChanged(position) } @@ -191,16 +188,11 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt override fun observeLiveBus() { observeEvent>(EventBus.SAVE_CONTENT) { (book, chapter) -> viewModel.bookData.value?.bookUrl?.let { bookUrl -> - if (book.bookUrl == bookUrl) { + if (book.bookUrl == viewModel.bookData.value?.bookUrl) { adapter.cacheFileNames.add(chapter.getFileName()) - if (viewModel.searchKey.isNullOrEmpty()) { - adapter.notifyItemChanged(chapter.index, true) - } else { - adapter.getItems().forEachIndexed { index, bookChapter -> - if (bookChapter.index == chapter.index) { - adapter.notifyItemChanged(index, true) - } - } + val index = adapter.getItems().indexOfFirst { it.index == chapter.index } + if (index >= 0) { + adapter.notifyItemChanged(index, true) } } } @@ -225,15 +217,9 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragment_chapt override fun onListChanged() { lifecycleScope.launch { - var scrollPos = 0 - withContext(Default) { - adapter.getItems().forEachIndexed { index, bookChapter -> - if (bookChapter.index >= durChapterIndex) { - return@withContext - } - scrollPos = index - } - } + val scrollPos = adapter.getItems() + .indexOfLast { it.index < durChapterIndex } + .coerceAtLeast(0) mLayoutManager.scrollToPositionWithOffset(scrollPos, 0) adapter.upDisplayTitles(scrollPos) } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/toc/TocActivity.kt b/app/src/main/java/io/legato/kazusa/ui/book/toc/TocActivity.kt index 0a05ebe2b..ff014e49a 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/toc/TocActivity.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/toc/TocActivity.kt @@ -60,8 +60,9 @@ class TocActivity : VMBaseActivity(), tabLayout = binding.tabLayout binding.viewPager.adapter = TabFragmentPageAdapter() tabLayout.setupWithViewPager(binding.viewPager) - viewModel.bookData.observe(this) { - menu?.setGroupVisible(R.id.menu_group_text, it.isLocalTxt) + viewModel.bookData.observe(this) { book -> + menu?.setGroupVisible(R.id.menu_group_text, book.isLocalTxt) + supportActionBar?.title = book.name } intent.getStringExtra("bookUrl")?.let { viewModel.initBook(it) diff --git a/app/src/main/res/drawable/ic_divider.xml b/app/src/main/res/drawable/ic_divider.xml index 21a88f21a..21fa91167 100644 --- a/app/src/main/res/drawable/ic_divider.xml +++ b/app/src/main/res/drawable/ic_divider.xml @@ -1,6 +1,10 @@ - - - - - + + + + + + + + diff --git a/app/src/main/res/drawable/ic_download_done.xml b/app/src/main/res/drawable/ic_download_done.xml new file mode 100644 index 000000000..a0a93c37c --- /dev/null +++ b/app/src/main/res/drawable/ic_download_done.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_chapter_list.xml b/app/src/main/res/layout/activity_chapter_list.xml index ce8620028..2809f62f2 100644 --- a/app/src/main/res/layout/activity_chapter_list.xml +++ b/app/src/main/res/layout/activity_chapter_list.xml @@ -19,7 +19,7 @@ android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" app:navigationIcon="@drawable/ic_arrow_back" - app:title="@string/chapter_list" /> + app:title="" /> + android:textSize="32sp" /> - + + + android:layout_gravity="center_horizontal" /> + + + + + android:paddingHorizontal="16dp" /> + android:layout_height="wrap_content"> + android:paddingBottom="8dp"> - + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_books.xml b/app/src/main/res/layout/fragment_books.xml index fd1fc4cee..1e431de7c 100644 --- a/app/src/main/res/layout/fragment_books.xml +++ b/app/src/main/res/layout/fragment_books.xml @@ -30,7 +30,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="(๑`^´๑)" - android:textSize="30sp"/> + android:textSize="32sp" /> - + + + + + + + + app:layout_constraintStart_toEndOf="@id/ll_info"> - + - + - + + -