Quellcode durchsuchen

* [修复] 透明主题阅读详情页FAB变黑和书架标签右侧半透明框问题 (@Piktowo) (#665)

Issue #659, #661

* [修复] 透明主题控件点击出现实色框问题,补全 surfaceContainer 系列颜色定义及主题映射 (@Piktowo) (#665) 

Issue #659

* 修复 TOC 自动定位竞态导致章节选取错误,修复书架共享元素返回动画位置错误 (@Piktowo) (#665) 

Issue #664, #647

* [修复] 阅读页背景Bitmap提前recycle导致的崩溃 (@Piktowo) (#665)
Piktowo vor 8 Monaten
Ursprung
Commit
2363e95702
19 geänderte Dateien mit 62 neuen und 25 gelöschten Zeilen
  1. 4 2
      app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt
  2. 1 1
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  3. 5 1
      app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt
  4. 16 5
      app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt
  5. 16 9
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BookshelfFragment1.kt
  6. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGrid.kt
  7. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCompact.kt
  8. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCover.kt
  9. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterList.kt
  10. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterListCompact.kt
  11. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGrid.kt
  12. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCompact.kt
  13. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCover.kt
  14. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterList.kt
  15. 1 0
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterListCompact.kt
  16. 4 4
      app/src/main/java/io/legado/app/ui/theme/colorScheme/TransparentColorScheme.kt
  17. 1 1
      app/src/main/res/values-night/colors.xml
  18. 2 2
      app/src/main/res/values/colors.xml
  19. 3 0
      app/src/main/res/values/themes.xml

+ 4 - 2
app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt

@@ -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() {

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt

@@ -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)
             }
         }
 

+ 5 - 1
app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt

@@ -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()
     }
 
     /**

+ 16 - 5
app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt

@@ -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
             }

+ 16 - 9
app/src/main/java/io/legado/app/ui/main/bookshelf/books/BookshelfFragment1.kt

@@ -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 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGrid.kt

@@ -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 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCompact.kt

@@ -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 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCover.kt

@@ -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 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterList.kt

@@ -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 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterListCompact.kt

@@ -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(

+ 1 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGrid.kt

@@ -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 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCompact.kt

@@ -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 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCover.kt

@@ -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)

+ 1 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterList.kt

@@ -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 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterListCompact.kt

@@ -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

+ 4 - 4
app/src/main/java/io/legado/app/ui/theme/colorScheme/TransparentColorScheme.kt

@@ -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),

+ 1 - 1
app/src/main/res/values-night/colors.xml

@@ -524,7 +524,7 @@
     <color name="transparent_theme_surfaceDim">#1AFFFFFF</color>
     <color name="transparent_theme_surfaceBright">#33FFFFFF</color>
     <color name="transparent_theme_surfaceContainerLowest">#0DFFFFFF</color>
-
+    <color name="transparent_theme_surfaceContainerLow">#1AFFFFFF</color>
     <color name="transparent_theme_surfaceContainer">#33FFFFFF</color>
     <color name="transparent_theme_surfaceContainerHigh">#4DFFFFFF</color>
     <color name="transparent_theme_surfaceContainerHighest">#66FFFFFF</color>

+ 2 - 2
app/src/main/res/values/colors.xml

@@ -498,7 +498,7 @@
     <color name="transparent_theme_secondaryContainer">#B0FFFFFF</color>
     <color name="transparent_theme_onSecondaryContainer">#FF000000</color>
     <color name="transparent_theme_tertiary">#000000</color>
-    <color name="transparent_theme_onTertiary">#FF000000</color>
+    <color name="transparent_theme_onTertiary">#FFFFFFFF</color>
     <color name="transparent_theme_tertiaryContainer">#B0FFFFFF</color>
     <color name="transparent_theme_onTertiaryContainer">#FF000000</color>
     <color name="transparent_theme_error">#BA1A1A</color>
@@ -532,7 +532,7 @@
     <color name="transparent_theme_surfaceDim">#1AFFFFFF</color>
     <color name="transparent_theme_surfaceBright">#33FFFFFF</color>
     <color name="transparent_theme_surfaceContainerLowest">#0DFFFFFF</color>
-
+    <color name="transparent_theme_surfaceContainerLow">#1AFFFFFF</color>
     <color name="transparent_theme_surfaceContainer">#33FFFFFF</color>
     <color name="transparent_theme_surfaceContainerHigh">#4DFFFFFF</color>
     <color name="transparent_theme_surfaceContainerHighest">#66FFFFFF</color>

+ 3 - 0
app/src/main/res/values/themes.xml

@@ -607,7 +607,10 @@
         <item name="colorSurfaceBright">@color/transparent_theme_surfaceBright</item>
         <item name="colorSurfaceContainerLowest">@color/transparent_theme_surfaceContainerLowest
         </item>
+        <item name="colorSurfaceContainerLow">@color/transparent_theme_surfaceContainerLow</item>
         <item name="colorSurfaceContainer">@color/transparent_theme_surfaceContainer</item>
+        <item name="colorSurfaceContainerHigh">@color/transparent_theme_surfaceContainerHigh</item>
+        <item name="colorSurfaceContainerHighest">@color/transparent_theme_surfaceContainerHighest</item>
     </style>
 
     <style name="Theme.Base.Elink" parent="Base.AppTheme">