diff --git a/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt index 7d31c7498..ec389c3ca 100644 --- a/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt +++ b/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() { diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index bfebcc6f1..6cab43195 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/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) } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt index 07b2ae669..8ce103f2d 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt +++ b/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() } /** diff --git a/app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt b/app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt index 18f06a54e..ed6ea8fbd 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt +++ b/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 } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BookshelfFragment1.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BookshelfFragment1.kt index 183416c48..cc62ff18a 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BookshelfFragment1.kt +++ b/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 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() { diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGrid.kt index 53c8e8b80..fa27e0a67 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGrid.kt +++ b/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 ) = 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) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCompact.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCompact.kt index 1fd375e23..9d42e9433 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCompact.kt +++ b/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 ) = 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) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCover.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCover.kt index 6c0aec437..fe7eac617 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterGridCover.kt +++ b/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 ) = 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) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterList.kt index 62ffc85b9..fc0de4105 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterList.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterList.kt @@ -35,6 +35,7 @@ class BooksAdapterList( payloads: MutableList ) = binding.run { if (payloads.isEmpty()) { + cdCover.transitionName = "book_${item.bookUrl}" tvName.text = item.name tvAuthor.text = item.author tvRead.text = item.durChapterTitle diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterListCompact.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterListCompact.kt index f101827b8..66b651f06 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterListCompact.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleDefalut/BooksAdapterListCompact.kt @@ -36,6 +36,7 @@ class BooksAdapterListCompact( payloads: MutableList ) = binding.run { if (payloads.isEmpty()) { + cdCover.transitionName = "book_${item.bookUrl}" tvName.text = item.name tvAuthor.text = context.getString( diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGrid.kt index ab126977c..9fdedbb29 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGrid.kt +++ b/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) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCompact.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCompact.kt index 9a95801f6..41419292b 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCompact.kt +++ b/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) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCover.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCover.kt index 91ea13d2f..1cd492cce 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterGridCover.kt +++ b/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) diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterList.kt index 671dab6d8..5a24ad01d 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterList.kt +++ b/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 diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterListCompact.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterListCompact.kt index dc7c6b952..7def24064 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/styleFold/BooksAdapterListCompact.kt +++ b/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 diff --git a/app/src/main/java/io/legado/app/ui/theme/colorScheme/TransparentColorScheme.kt b/app/src/main/java/io/legado/app/ui/theme/colorScheme/TransparentColorScheme.kt index 6b38b60f4..58f30bb05 100644 --- a/app/src/main/java/io/legado/app/ui/theme/colorScheme/TransparentColorScheme.kt +++ b/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), diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index ca6406315..edc68b7c6 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -524,7 +524,7 @@ #1AFFFFFF #33FFFFFF #0DFFFFFF - + #1AFFFFFF #33FFFFFF #4DFFFFFF #66FFFFFF diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1a604ad0e..4763b563a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -498,7 +498,7 @@ #B0FFFFFF #FF000000 #000000 - #FF000000 + #FFFFFFFF #B0FFFFFF #FF000000 #BA1A1A @@ -532,7 +532,7 @@ #1AFFFFFF #33FFFFFF #0DFFFFFF - + #1AFFFFFF #33FFFFFF #4DFFFFFF #66FFFFFF diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 2277d0885..e2bb67efe 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -607,7 +607,10 @@ @color/transparent_theme_surfaceBright @color/transparent_theme_surfaceContainerLowest + @color/transparent_theme_surfaceContainerLow @color/transparent_theme_surfaceContainer + @color/transparent_theme_surfaceContainerHigh + @color/transparent_theme_surfaceContainerHighest