更新界面
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<TocViewModel>(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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>()
|
||||
private val displayTitleMap = ConcurrentHashMap<String, String>()
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
override val diffItemCallback: DiffUtil.ItemCallback<BookChapter>
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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<TocViewModel>(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<TocViewModel>(R.layout.fragment_chapt
|
||||
override fun observeLiveBus() {
|
||||
observeEvent<Pair<Book, BookChapter>>(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<TocViewModel>(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)
|
||||
}
|
||||
|
||||
@@ -60,8 +60,9 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user