Просмотр исходного кода

[优化] 修复封面进入时尺寸错误的问题,添加分割线选项

HapeLee 7 месяцев назад
Родитель
Сommit
63e1db17b1

+ 4 - 1
app/src/main/java/io/legado/app/model/BookCover.kt

@@ -91,11 +91,14 @@ object BookCover {
         val cacheKey = "$randomPath-${isNight}"
         val cacheKey = "$randomPath-${isNight}"
 
 
         // 从缓存中获取,如果没有则解码并缓存
         // 从缓存中获取,如果没有则解码并缓存
-        return randomDrawableCache.getOrPut(cacheKey) {
+        val drawable = randomDrawableCache.getOrPut(cacheKey) {
             kotlin.runCatching {
             kotlin.runCatching {
                 BitmapUtils.decodeBitmap(randomPath, 600, 900)!!.toDrawable(appCtx.resources)
                 BitmapUtils.decodeBitmap(randomPath, 600, 900)!!.toDrawable(appCtx.resources)
             }.getOrDefault(appCtx.resources.getDrawable(R.drawable.image_cover_default, null))
             }.getOrDefault(appCtx.resources.getDrawable(R.drawable.image_cover_default, null))
         }
         }
+
+        // 返回克隆的实例并 mutate,防止多个 View 共享状态(如 bounds)导致显示异常
+        return drawable.constantState?.newDrawable()?.mutate() ?: drawable
     }
     }
 
 
     /**
     /**

+ 5 - 0
app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt

@@ -98,6 +98,11 @@ object BookshelfConfig {
      */
      */
     var bookshelfLayoutCompact by prefDelegate(PreferKey.bookshelfLayoutCompact, false)
     var bookshelfLayoutCompact by prefDelegate(PreferKey.bookshelfLayoutCompact, false)
 
 
+    /**
+     * 是否显示书架分隔线
+     */
+    var bookshelfShowDivider by prefDelegate(PreferKey.bookshelfShowDivider, true)
+
     /**
     /**
      * 书架标题小字体
      * 书架标题小字体
      */
      */

+ 62 - 70
app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt

@@ -75,6 +75,8 @@ fun BookshelfItem(
 
 
                 Box(
                 Box(
                     modifier = Modifier
                     modifier = Modifier
+                        .fillMaxWidth()
+                        .aspectRatio(5f / 7f)
                         .then(
                         .then(
                             if (coverShadow) Modifier.shadow(
                             if (coverShadow) Modifier.shadow(
                                 4.dp,
                                 4.dp,
@@ -83,7 +85,7 @@ fun BookshelfItem(
                         )
                         )
                         .clip(MaterialTheme.shapes.extraSmall) // 先阴影后裁剪
                         .clip(MaterialTheme.shapes.extraSmall) // 先阴影后裁剪
                 ) {
                 ) {
-                    cover(Modifier.fillMaxWidth())
+                    cover(Modifier.fillMaxSize())
                     if (gridStyle == 1) {
                     if (gridStyle == 1) {
                         Text(
                         Text(
                             text = title,
                             text = title,
@@ -184,7 +186,7 @@ fun BookshelfItem(
                     }
                     }
                 }
                 }
             }
             }
-            if (!isCompact) HorizontalDivider(
+            if (BookshelfConfig.bookshelfShowDivider) HorizontalDivider(
                 modifier = Modifier.padding(horizontal = 16.dp),
                 modifier = Modifier.padding(horizontal = 16.dp),
                 thickness = 0.5.dp,
                 thickness = 0.5.dp,
                 color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f)
                 color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f)
@@ -205,78 +207,68 @@ fun BookGroupCover(
             .clip(RoundedCornerShape(4.dp))
             .clip(RoundedCornerShape(4.dp))
             .background(MaterialTheme.colorScheme.surfaceContainer)
             .background(MaterialTheme.colorScheme.surfaceContainer)
     ) {
     ) {
-        if (coverPath != null) {
-            BookCover(
-                name = "",
-                author = "",
-                path = coverPath,
-                modifier = Modifier.fillMaxSize(),
-                ignoreUseDefaultCover = true
-            )
-        } else {
-            Column(modifier = Modifier.fillMaxSize()) {
-                Row(modifier = Modifier.weight(1f)) {
-                    Box(
-                        modifier = Modifier
-                            .weight(1f)
-                            .fillMaxHeight()
-                            .padding(1.dp)
-                    ) {
-                        books.getOrNull(0)?.let {
-                            BookCover(
-                                name = it.name,
-                                author = it.author,
-                                path = it.getDisplayCover(),
-                                modifier = Modifier.fillMaxSize()
-                            )
-                        }
+        Column(modifier = Modifier.fillMaxSize()) {
+            Row(modifier = Modifier.weight(1f)) {
+                Box(
+                    modifier = Modifier
+                        .weight(1f)
+                        .fillMaxHeight()
+                        .padding(1.dp)
+                ) {
+                    books.getOrNull(0)?.let {
+                        BookCover(
+                            name = it.name,
+                            author = it.author,
+                            path = it.getDisplayCover(),
+                            modifier = Modifier.fillMaxSize()
+                        )
                     }
                     }
-                    Box(
-                        modifier = Modifier
-                            .weight(1f)
-                            .fillMaxHeight()
-                            .padding(1.dp)
-                    ) {
-                        books.getOrNull(1)?.let {
-                            BookCover(
-                                name = it.name,
-                                author = it.author,
-                                path = it.getDisplayCover(),
-                                modifier = Modifier.fillMaxSize()
-                            )
-                        }
+                }
+                Box(
+                    modifier = Modifier
+                        .weight(1f)
+                        .fillMaxHeight()
+                        .padding(1.dp)
+                ) {
+                    books.getOrNull(1)?.let {
+                        BookCover(
+                            name = it.name,
+                            author = it.author,
+                            path = it.getDisplayCover(),
+                            modifier = Modifier.fillMaxSize()
+                        )
                     }
                     }
                 }
                 }
-                Row(modifier = Modifier.weight(1f)) {
-                    Box(
-                        modifier = Modifier
-                            .weight(1f)
-                            .fillMaxHeight()
-                            .padding(1.dp)
-                    ) {
-                        books.getOrNull(2)?.let {
-                            BookCover(
-                                name = it.name,
-                                author = it.author,
-                                path = it.getDisplayCover(),
-                                modifier = Modifier.fillMaxSize()
-                            )
-                        }
+            }
+            Row(modifier = Modifier.weight(1f)) {
+                Box(
+                    modifier = Modifier
+                        .weight(1f)
+                        .fillMaxHeight()
+                        .padding(1.dp)
+                ) {
+                    books.getOrNull(2)?.let {
+                        BookCover(
+                            name = it.name,
+                            author = it.author,
+                            path = it.getDisplayCover(),
+                            modifier = Modifier.fillMaxSize()
+                        )
                     }
                     }
-                    Box(
-                        modifier = Modifier
-                            .weight(1f)
-                            .fillMaxHeight()
-                            .padding(1.dp)
-                    ) {
-                        books.getOrNull(3)?.let {
-                            BookCover(
-                                name = it.name,
-                                author = it.author,
-                                path = it.getDisplayCover(),
-                                modifier = Modifier.fillMaxSize()
-                            )
-                        }
+                }
+                Box(
+                    modifier = Modifier
+                        .weight(1f)
+                        .fillMaxHeight()
+                        .padding(1.dp)
+                ) {
+                    books.getOrNull(3)?.let {
+                        BookCover(
+                            name = it.name,
+                            author = it.author,
+                            path = it.getDisplayCover(),
+                            modifier = Modifier.fillMaxSize()
+                        )
                     }
                     }
                 }
                 }
             }
             }

+ 21 - 15
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt

@@ -118,6 +118,20 @@ fun BookshelfConfigSheet(
                     imageVector = Icons.Default.ViewCompact,
                     imageVector = Icons.Default.ViewCompact,
                     onValueChange = { BookshelfConfig.bookshelfGridLayout = it.toInt() }
                     onValueChange = { BookshelfConfig.bookshelfGridLayout = it.toInt() }
                 )
                 )
+                CompactSwitchSettingItem(
+                    title = "标题小字体",
+                    checked = BookshelfConfig.bookshelfTitleSmallFont,
+                    imageVector = Icons.Default.TextFormat,
+                    color = MaterialTheme.colorScheme.surface,
+                    onCheckedChange = { BookshelfConfig.bookshelfTitleSmallFont = it }
+                )
+
+                CompactSwitchSettingItem(
+                    title = "标题居中",
+                    checked = BookshelfConfig.bookshelfTitleCenter,
+                    color = MaterialTheme.colorScheme.surface,
+                    onCheckedChange = { BookshelfConfig.bookshelfTitleCenter = it }
+                )
             } else {
             } else {
                 CompactSwitchSettingItem(
                 CompactSwitchSettingItem(
                     title = "紧凑模式",
                     title = "紧凑模式",
@@ -126,6 +140,13 @@ fun BookshelfConfigSheet(
                     color = MaterialTheme.colorScheme.surface,
                     color = MaterialTheme.colorScheme.surface,
                     onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
                     onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
                 )
                 )
+                CompactSwitchSettingItem(
+                    title = "显示分隔线",
+                    checked = BookshelfConfig.bookshelfShowDivider,
+                    imageVector = Icons.Default.ViewCompact,
+                    color = MaterialTheme.colorScheme.surface,
+                    onCheckedChange = { BookshelfConfig.bookshelfShowDivider = it }
+                )
             }
             }
 
 
             // Grid Count
             // Grid Count
@@ -142,21 +163,6 @@ fun BookshelfConfigSheet(
                 }
                 }
             )
             )
 
 
-            CompactSwitchSettingItem(
-                title = "标题小字体",
-                checked = BookshelfConfig.bookshelfTitleSmallFont,
-                imageVector = Icons.Default.TextFormat,
-                color = MaterialTheme.colorScheme.surface,
-                onCheckedChange = { BookshelfConfig.bookshelfTitleSmallFont = it }
-            )
-
-            CompactSwitchSettingItem(
-                title = "标题居中",
-                checked = BookshelfConfig.bookshelfTitleCenter,
-                color = MaterialTheme.colorScheme.surface,
-                onCheckedChange = { BookshelfConfig.bookshelfTitleCenter = it }
-            )
-
             CompactSliderSettingItem(
             CompactSliderSettingItem(
                 title = "标题最大行数",
                 title = "标题最大行数",
                 value = BookshelfConfig.bookshelfTitleMaxLines.toFloat(),
                 value = BookshelfConfig.bookshelfTitleMaxLines.toFloat(),

+ 37 - 52
app/src/main/java/io/legado/app/ui/widget/components/cover/BookCover.kt

@@ -19,6 +19,7 @@ import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Update
 import androidx.compose.material.icons.filled.Update
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.remember
@@ -37,10 +38,10 @@ import androidx.compose.ui.unit.dp
 import androidx.core.graphics.withSave
 import androidx.core.graphics.withSave
 import coil.compose.AsyncImage
 import coil.compose.AsyncImage
 import coil.request.ImageRequest
 import coil.request.ImageRequest
-import io.legado.app.model.BookCover
 import io.legado.app.ui.config.coverConfig.CoverConfig
 import io.legado.app.ui.config.coverConfig.CoverConfig
 import io.legado.app.ui.widget.components.card.TextCard
 import io.legado.app.ui.widget.components.card.TextCard
 import org.koin.compose.koinInject
 import org.koin.compose.koinInject
+import io.legado.app.model.BookCover as BookCoverModel
 
 
 @Composable
 @Composable
 fun BookCover(
 fun BookCover(
@@ -60,34 +61,23 @@ fun BookCover(
     val useDefault = !ignoreUseDefaultCover && CoverConfig.useDefaultCover
     val useDefault = !ignoreUseDefaultCover && CoverConfig.useDefaultCover
     val finalPath = if (useDefault) null else path
     val finalPath = if (useDefault) null else path
 
 
-    // 获取随机封面路径(字符串),增加配置项作为 Key,确保设置更改后立即刷新
     val randomPath = remember(
     val randomPath = remember(
         name, author, path, isNight,
         name, author, path, isNight,
         CoverConfig.defaultCover,
         CoverConfig.defaultCover,
         CoverConfig.defaultCoverDark
         CoverConfig.defaultCoverDark
     ) {
     ) {
-        BookCover.getRandomDefaultPath(
+        BookCoverModel.getRandomDefaultPath(
             seed = name ?: author ?: path ?: "",
             seed = name ?: author ?: path ?: "",
             isNight = isNight
             isNight = isNight
         )
         )
     }
     }
 
 
-    // 获取随机封面 Drawable,用于占位图
-    val randomDrawable = remember(
-        name, author, path, isNight,
-        CoverConfig.defaultCover,
-        CoverConfig.defaultCoverDark
-    ) {
-        BookCover.getRandomDefaultDrawable(
-            seed = name ?: author ?: path ?: "",
-            isNight = isNight
-        )
-    }
-
-    // 检查是否有自定义随机封面设置
     val hasCustomDefault = !randomPath.isNullOrBlank()
     val hasCustomDefault = !randomPath.isNullOrBlank()
 
 
     var isOnlineCoverLoaded by remember(path) { mutableStateOf(false) }
     var isOnlineCoverLoaded by remember(path) { mutableStateOf(false) }
+    LaunchedEffect(finalPath) {
+        isOnlineCoverLoaded = false
+    }
 
 
     Box(
     Box(
         modifier = modifier
         modifier = modifier
@@ -104,49 +94,46 @@ fun BookCover(
                 } else Modifier
                 } else Modifier
             )
             )
     ) {
     ) {
-        // 如果没有自定义随机封面,显示书本图标作为底图
-        // TODO:暂时先用猫猫头
-        /*
-        if (!hasCustomDefault) {
-            Icon(
-                Icons.Default.Book,
+        if (hasCustomDefault) {
+            AsyncImage(
+                model = ImageRequest.Builder(context)
+                    .data(randomPath)
+                    .crossfade(true)
+                    .build(),
                 contentDescription = null,
                 contentDescription = null,
-                tint = MaterialTheme.colorScheme.secondary,
-                modifier = Modifier
-                    .size(32.dp)
-                    .align(Alignment.Center)
+                imageLoader = koinInject(),
+                contentScale = ContentScale.Crop,
+                modifier = Modifier.fillMaxSize()
             )
             )
         }
         }
-         */
-
 
 
-        // 1. 封面图层
-        AsyncImage(
-            model = ImageRequest.Builder(context)
-                .data(finalPath ?: randomPath)
-                .placeholder(randomDrawable)
-                .error(randomDrawable)
-                .crossfade(true)
-                .setParameter("sourceOrigin", sourceOrigin)
-                .setParameter("loadOnlyWifi", CoverConfig.loadCoverOnlyWifi)
-                .build(),
-            contentDescription = null,
-            imageLoader = koinInject(),
-            contentScale = ContentScale.Crop,
-            modifier = Modifier.fillMaxSize(),
-            onSuccess = {
-                if (finalPath != null) {
+        if (finalPath != null) {
+            AsyncImage(
+                model = ImageRequest.Builder(context)
+                    .data(finalPath)
+                    .crossfade(true)
+                    .setParameter("sourceOrigin", sourceOrigin)
+                    .setParameter("loadOnlyWifi", CoverConfig.loadCoverOnlyWifi)
+                    .build(),
+                contentDescription = null,
+                imageLoader = koinInject(),
+                contentScale = ContentScale.Crop,
+                modifier = Modifier.fillMaxSize(),
+                onSuccess = {
                     isOnlineCoverLoaded = true
                     isOnlineCoverLoaded = true
+                    onLoadFinish?.invoke()
+                },
+                onError = {
+                    isOnlineCoverLoaded = false
+                    onLoadFinish?.invoke()
                 }
                 }
-                onLoadFinish?.invoke()
-            },
-            onError = {
-                isOnlineCoverLoaded = false
+            )
+        } else {
+            LaunchedEffect(Unit) {
                 onLoadFinish?.invoke()
                 onLoadFinish?.invoke()
             }
             }
-        )
+        }
 
 
-        // 2. 文字叠加层:当没有书籍自身封面(或加载失败)时,在随机底图上绘制书名/作者
         if (!isOnlineCoverLoaded) {
         if (!isOnlineCoverLoaded) {
             CoverTextOverlay(
             CoverTextOverlay(
                 name = name,
                 name = name,
@@ -155,7 +142,6 @@ fun BookCover(
             )
             )
         }
         }
 
 
-        // 3. 角标 (Badge)
         if (!badgeText.isNullOrEmpty()) {
         if (!badgeText.isNullOrEmpty()) {
             TextCard(
             TextCard(
                 text = badgeText,
                 text = badgeText,
@@ -218,7 +204,6 @@ private fun CoverTextOverlay(
                     val textPaint = TextPaint(paint).apply {
                     val textPaint = TextPaint(paint).apply {
                         textAlign = Paint.Align.LEFT
                         textAlign = Paint.Align.LEFT
                     }
                     }
-
                     val layout = StaticLayout.Builder
                     val layout = StaticLayout.Builder
                         .obtain(name, 0, name.length, textPaint, maxWidth)
                         .obtain(name, 0, name.length, textPaint, maxWidth)
                         .setAlignment(Layout.Alignment.ALIGN_CENTER)
                         .setAlignment(Layout.Alignment.ALIGN_CENTER)