* [修复] 透明主题阅读详情页FAB变黑和书架标签右侧半透明框问题 (@Piktowo) (#665)
Issue #659, #661 * [修复] 透明主题控件点击出现实色框问题,补全 surfaceContainer 系列颜色定义及主题映射 (@Piktowo) (#665) Issue #659 * 修复 TOC 自动定位竞态导致章节选取错误,修复书架共享元素返回动画位置错误 (@Piktowo) (#665) Issue #664, #647 * [修复] 阅读页背景Bitmap提前recycle导致的崩溃 (@Piktowo) (#665)
This commit is contained in:
@@ -106,7 +106,7 @@ object ReadBookConfig {
|
||||
shareConfig = c ?: configList.getOrNull(5) ?: Config()
|
||||
}
|
||||
|
||||
fun upBg(width: Int, height: Int) {
|
||||
fun upBg(width: Int, height: Int): Drawable? {
|
||||
val drawable = durConfig.curBgDrawable(width, height)
|
||||
if (drawable is BitmapDrawable && drawable.bitmap != null) {
|
||||
bgMeanColor = drawable.bitmap.getMeanColor()
|
||||
@@ -115,7 +115,9 @@ object ReadBookConfig {
|
||||
}
|
||||
val tmp = bg
|
||||
bg = drawable
|
||||
(tmp as? BitmapDrawable)?.bitmap?.recycle()
|
||||
// 返回旧 Drawable,由调用方在视图更新完成后再 recycle,
|
||||
// 避免视图仍引用旧 BitmapDrawable 时 bitmap 已被 recycle 导致崩溃
|
||||
return tmp
|
||||
}
|
||||
|
||||
fun save() {
|
||||
|
||||
@@ -660,7 +660,7 @@ class BookInfoActivity :
|
||||
duration = 400L
|
||||
addUpdateListener { animation ->
|
||||
val color = animation.animatedValue as Int
|
||||
binding.btnRead.setBackgroundColor(color)
|
||||
binding.btnRead.backgroundTintList = ColorStateList.valueOf(color)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.RectF
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
@@ -616,10 +617,13 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
* 更新背景
|
||||
*/
|
||||
fun upBg() {
|
||||
ReadBookConfig.upBg(width, height)
|
||||
val oldBg = ReadBookConfig.upBg(width, height)
|
||||
curPage.upBg()
|
||||
prevPage.upBg()
|
||||
nextPage.upBg()
|
||||
// 所有视图背景更新完成后再 recycle 旧 bitmap,
|
||||
// 防止视图仍持有旧 BitmapDrawable 引用时 bitmap 已被 recycle 导致崩溃
|
||||
(oldBg as? BitmapDrawable)?.bitmap?.recycle()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,6 +110,8 @@ import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||
import io.legado.app.ui.widget.components.topbar.DynamicTopAppBar
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -229,13 +231,22 @@ fun TocScreen(
|
||||
|
||||
var hasAutoScrolled by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(state.items) {
|
||||
if (!hasAutoScrolled && state.items.isNotEmpty()) {
|
||||
val targetIndex = state.items.indexOfFirst { it.isDur }
|
||||
LaunchedEffect(Unit) {
|
||||
if (!hasAutoScrolled) {
|
||||
// 等待列表包含当前阅读章节后自动定位,避免在无 isDur 项时提前退出
|
||||
// 使用 animateScrollToItem 以便用户看到滚动动画,避免误操作
|
||||
val items = snapshotFlow { state.items }
|
||||
.filter { list -> list.any { it.isDur } }
|
||||
.first()
|
||||
val targetIndex = items.indexOfFirst { it.isDur }
|
||||
if (targetIndex != -1) {
|
||||
listState.scrollToItem(
|
||||
// 等待视口尺寸测量完成,确保 offset 计算正确
|
||||
snapshotFlow { listState.layoutInfo.viewportEndOffset }
|
||||
.filter { it > 0 }
|
||||
.first()
|
||||
listState.animateScrollToItem(
|
||||
index = targetIndex,
|
||||
scrollOffset = -offset
|
||||
scrollOffset = -(listState.layoutInfo.viewportEndOffset / 4)
|
||||
)
|
||||
hasAutoScrolled = true
|
||||
}
|
||||
|
||||
@@ -10,8 +10,10 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import io.legado.app.R
|
||||
@@ -22,6 +24,7 @@ import io.legado.app.databinding.FragmentBookshelf1Binding
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.ui.book.group.GroupEditDialog
|
||||
import io.legado.app.ui.book.search.SearchActivity
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
import io.legado.app.ui.main.bookshelf.BaseBookshelfFragment
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.themeColor
|
||||
@@ -49,6 +52,7 @@ class BookshelfFragment1() : BaseBookshelfFragment(R.layout.fragment_bookshelf1)
|
||||
override val groupId: Long get() = selectedGroup?.groupId ?: 0
|
||||
|
||||
private lateinit var adapter: TabFragmentPageAdapter
|
||||
private var tabFadeListener: AppBarLayout.OnOffsetChangedListener? = null
|
||||
|
||||
override val books: List<Book>
|
||||
get() {
|
||||
@@ -87,17 +91,20 @@ class BookshelfFragment1() : BaseBookshelfFragment(R.layout.fragment_bookshelf1)
|
||||
}
|
||||
|
||||
private fun updateTabFadeEffect() {
|
||||
binding.titleBar.addOnOffsetChangedListener({ appBarLayout, verticalOffset ->
|
||||
if (-verticalOffset >= appBarLayout.totalScrollRange) {
|
||||
val drawable = binding.tabRightFade.background as? GradientDrawable
|
||||
drawable?.colors = intArrayOf(Color.TRANSPARENT,
|
||||
requireContext().themeColor(com.google.android.material.R.attr.colorSurfaceContainer))
|
||||
tabFadeListener?.let { binding.titleBar.removeOnOffsetChangedListener(it) }
|
||||
val listener = AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
|
||||
val drawable = binding.tabRightFade.background as? GradientDrawable
|
||||
val opacity = if (ThemeConfig.enableBlur) ThemeConfig.containerOpacity / 100f else 1f
|
||||
val baseColor = if (-verticalOffset >= appBarLayout.totalScrollRange) {
|
||||
requireContext().themeColor(com.google.android.material.R.attr.colorSurfaceContainer)
|
||||
} else {
|
||||
val drawable = binding.tabRightFade.background as? GradientDrawable
|
||||
drawable?.colors = intArrayOf(Color.TRANSPARENT,
|
||||
requireContext().themeColor(com.google.android.material.R.attr.colorSurface))
|
||||
requireContext().themeColor(com.google.android.material.R.attr.colorSurface)
|
||||
}
|
||||
})
|
||||
val newAlpha = (Color.alpha(baseColor) * opacity).toInt()
|
||||
drawable?.colors = intArrayOf(Color.TRANSPARENT, ColorUtils.setAlphaComponent(baseColor, newAlpha))
|
||||
}
|
||||
tabFadeListener = listener
|
||||
binding.titleBar.addOnOffsetChangedListener(listener)
|
||||
}
|
||||
|
||||
private fun showTabSelectionMenu() {
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) :
|
||||
payloads: MutableList<Any>
|
||||
) = binding.run {
|
||||
if (payloads.isEmpty()) {
|
||||
cdCover.transitionName = "book_${item.bookUrl}"
|
||||
tvName.text = item.name
|
||||
ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
|
||||
upRefresh(binding, item)
|
||||
|
||||
+1
@@ -28,6 +28,7 @@ class BooksAdapterGridCompact(context: Context, private val callBack: CallBack)
|
||||
payloads: MutableList<Any>
|
||||
) = binding.run {
|
||||
if (payloads.isEmpty()) {
|
||||
cvContent.transitionName = "book_${item.bookUrl}"
|
||||
tvName.text = item.name
|
||||
ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
|
||||
upRefresh(binding, item)
|
||||
|
||||
+1
@@ -28,6 +28,7 @@ class BooksAdapterGridCover(context: Context, private val callBack: CallBack) :
|
||||
payloads: MutableList<Any>
|
||||
) = binding.run {
|
||||
if (payloads.isEmpty()) {
|
||||
cvContent.transitionName = "book_${item.bookUrl}"
|
||||
tvName.gone()
|
||||
ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
|
||||
upRefresh(binding, item)
|
||||
|
||||
+1
@@ -35,6 +35,7 @@ class BooksAdapterList(
|
||||
payloads: MutableList<Any>
|
||||
) = binding.run {
|
||||
if (payloads.isEmpty()) {
|
||||
cdCover.transitionName = "book_${item.bookUrl}"
|
||||
tvName.text = item.name
|
||||
tvAuthor.text = item.author
|
||||
tvRead.text = item.durChapterTitle
|
||||
|
||||
+1
@@ -36,6 +36,7 @@ class BooksAdapterListCompact(
|
||||
payloads: MutableList<Any>
|
||||
) = binding.run {
|
||||
if (payloads.isEmpty()) {
|
||||
cdCover.transitionName = "book_${item.bookUrl}"
|
||||
tvName.text = item.name
|
||||
tvAuthor.text =
|
||||
context.getString(
|
||||
|
||||
@@ -64,6 +64,7 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun onBind(item: Book) = binding.run {
|
||||
cvContent.transitionName = "book_${item.bookUrl}"
|
||||
tvName.text = item.name
|
||||
ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
|
||||
upRefresh(this, item)
|
||||
|
||||
+1
@@ -71,6 +71,7 @@ class BooksAdapterGridCompact(context: Context, callBack: CallBack) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun onBind(item: Book) = binding.run {
|
||||
cvContent.transitionName = "book_${item.bookUrl}"
|
||||
tvName.text = item.name
|
||||
ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
|
||||
upRefresh(this, item)
|
||||
|
||||
+1
@@ -71,6 +71,7 @@ class BooksAdapterGridCover(context: Context, callBack: CallBack) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun onBind(item: Book) = binding.run {
|
||||
cvContent.transitionName = "book_${item.bookUrl}"
|
||||
tvName.gone()
|
||||
ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
|
||||
upRefresh(this, item)
|
||||
|
||||
@@ -60,6 +60,7 @@ class BooksAdapterList(context: Context, callBack: CallBack) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun onBind(item: Book) = binding.run {
|
||||
cdCover.transitionName = "book_${item.bookUrl}"
|
||||
tvName.text = item.name
|
||||
tvAuthor.text = item.author
|
||||
tvRead.text = item.durChapterTitle
|
||||
|
||||
+1
@@ -60,6 +60,7 @@ class BooksAdapterListCompact(context: Context, callBack: CallBack) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun onBind(item: Book) = binding.run {
|
||||
cdCover.transitionName = "book_${item.bookUrl}"
|
||||
tvName.text = item.name
|
||||
tvAuthor.text = item.author
|
||||
tvLast.text = item.latestChapterTitle
|
||||
|
||||
@@ -17,7 +17,7 @@ internal object TransparentColorScheme : BaseColorScheme() {
|
||||
secondaryContainer = Color(0xB0FFFFFF),
|
||||
onSecondaryContainer = Color(0xFF000000),
|
||||
tertiary = Color(0xFF000000),
|
||||
onTertiary = Color(0xFF000000),
|
||||
onTertiary = Color(0xFFFFFFFF),
|
||||
tertiaryContainer = Color(0xB0FFFFFF),
|
||||
onTertiaryContainer = Color(0xFF000000),
|
||||
error = Color(0xFFBA1A1A),
|
||||
@@ -51,7 +51,7 @@ internal object TransparentColorScheme : BaseColorScheme() {
|
||||
surfaceDim = Color(0x1AFFFFFF),
|
||||
surfaceBright = Color(0x33FFFFFF),
|
||||
surfaceContainerLowest = Color(0x0DFFFFFF),
|
||||
surfaceContainerLow = Color(0x8FFFFFFF),
|
||||
surfaceContainerLow = Color(0x1AFFFFFF),
|
||||
surfaceContainer = Color(0x33FFFFFF),
|
||||
surfaceContainerHigh = Color(0x4DFFFFFF),
|
||||
surfaceContainerHighest = Color(0x66FFFFFF),
|
||||
@@ -67,7 +67,7 @@ internal object TransparentColorScheme : BaseColorScheme() {
|
||||
secondaryContainer = Color(0xA0FFFFFF),
|
||||
onSecondaryContainer = Color(0xFF000000),
|
||||
tertiary = Color(0x80FFFFFF),
|
||||
onTertiary = Color(0xFF000000),
|
||||
onTertiary = Color(0xFFFFFFFF),
|
||||
tertiaryContainer = Color(0x40FFFFFF),
|
||||
onTertiaryContainer = Color(0xFF000000),
|
||||
error = Color(0xFFCF6679),
|
||||
@@ -101,7 +101,7 @@ internal object TransparentColorScheme : BaseColorScheme() {
|
||||
surfaceDim = Color(0x1AFFFFFF),
|
||||
surfaceBright = Color(0x33FFFFFF),
|
||||
surfaceContainerLowest = Color(0x0DFFFFFF),
|
||||
surfaceContainerLow = Color(0x8F000000),
|
||||
surfaceContainerLow = Color(0x1AFFFFFF),
|
||||
surfaceContainer = Color(0x33FFFFFF),
|
||||
surfaceContainerHigh = Color(0x4DFFFFFF),
|
||||
surfaceContainerHighest = Color(0x66FFFFFF),
|
||||
|
||||
Reference in New Issue
Block a user