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

This commit is contained in:
HapeLee
2026-03-27 01:30:13 +08:00
parent 75a3752079
commit 63e1db17b1
5 changed files with 130 additions and 139 deletions
@@ -91,11 +91,14 @@ object BookCover {
val cacheKey = "$randomPath-${isNight}"
// 从缓存中获取,如果没有则解码并缓存
return randomDrawableCache.getOrPut(cacheKey) {
val drawable = randomDrawableCache.getOrPut(cacheKey) {
kotlin.runCatching {
BitmapUtils.decodeBitmap(randomPath, 600, 900)!!.toDrawable(appCtx.resources)
}.getOrDefault(appCtx.resources.getDrawable(R.drawable.image_cover_default, null))
}
// 返回克隆的实例并 mutate,防止多个 View 共享状态(如 bounds)导致显示异常
return drawable.constantState?.newDrawable()?.mutate() ?: drawable
}
/**
@@ -98,6 +98,11 @@ object BookshelfConfig {
*/
var bookshelfLayoutCompact by prefDelegate(PreferKey.bookshelfLayoutCompact, false)
/**
* 是否显示书架分隔线
*/
var bookshelfShowDivider by prefDelegate(PreferKey.bookshelfShowDivider, true)
/**
* 书架标题小字体
*/
@@ -75,6 +75,8 @@ fun BookshelfItem(
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(5f / 7f)
.then(
if (coverShadow) Modifier.shadow(
4.dp,
@@ -83,7 +85,7 @@ fun BookshelfItem(
)
.clip(MaterialTheme.shapes.extraSmall) // 先阴影后裁剪
) {
cover(Modifier.fillMaxWidth())
cover(Modifier.fillMaxSize())
if (gridStyle == 1) {
Text(
text = title,
@@ -184,7 +186,7 @@ fun BookshelfItem(
}
}
}
if (!isCompact) HorizontalDivider(
if (BookshelfConfig.bookshelfShowDivider) HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp),
thickness = 0.5.dp,
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f)
@@ -205,78 +207,68 @@ fun BookGroupCover(
.clip(RoundedCornerShape(4.dp))
.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()
)
}
}
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()
)
}
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()
)
}
}
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(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(3)?.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()
)
}
}
}
@@ -118,6 +118,20 @@ fun BookshelfConfigSheet(
imageVector = Icons.Default.ViewCompact,
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 {
CompactSwitchSettingItem(
title = "紧凑模式",
@@ -126,6 +140,13 @@ fun BookshelfConfigSheet(
color = MaterialTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
)
CompactSwitchSettingItem(
title = "显示分隔线",
checked = BookshelfConfig.bookshelfShowDivider,
imageVector = Icons.Default.ViewCompact,
color = MaterialTheme.colorScheme.surface,
onCheckedChange = { BookshelfConfig.bookshelfShowDivider = it }
)
}
// 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(
title = "标题最大行数",
value = BookshelfConfig.bookshelfTitleMaxLines.toFloat(),
@@ -19,6 +19,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Update
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -37,10 +38,10 @@ import androidx.compose.ui.unit.dp
import androidx.core.graphics.withSave
import coil.compose.AsyncImage
import coil.request.ImageRequest
import io.legado.app.model.BookCover
import io.legado.app.ui.config.coverConfig.CoverConfig
import io.legado.app.ui.widget.components.card.TextCard
import org.koin.compose.koinInject
import io.legado.app.model.BookCover as BookCoverModel
@Composable
fun BookCover(
@@ -60,34 +61,23 @@ fun BookCover(
val useDefault = !ignoreUseDefaultCover && CoverConfig.useDefaultCover
val finalPath = if (useDefault) null else path
// 获取随机封面路径(字符串),增加配置项作为 Key,确保设置更改后立即刷新
val randomPath = remember(
name, author, path, isNight,
CoverConfig.defaultCover,
CoverConfig.defaultCoverDark
) {
BookCover.getRandomDefaultPath(
BookCoverModel.getRandomDefaultPath(
seed = name ?: author ?: path ?: "",
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()
var isOnlineCoverLoaded by remember(path) { mutableStateOf(false) }
LaunchedEffect(finalPath) {
isOnlineCoverLoaded = false
}
Box(
modifier = modifier
@@ -104,49 +94,46 @@ fun BookCover(
} else Modifier
)
) {
// 如果没有自定义随机封面,显示书本图标作为底图
// TODO:暂时先用猫猫头
/*
if (!hasCustomDefault) {
Icon(
Icons.Default.Book,
if (hasCustomDefault) {
AsyncImage(
model = ImageRequest.Builder(context)
.data(randomPath)
.crossfade(true)
.build(),
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
onLoadFinish?.invoke()
},
onError = {
isOnlineCoverLoaded = false
onLoadFinish?.invoke()
}
onLoadFinish?.invoke()
},
onError = {
isOnlineCoverLoaded = false
)
} else {
LaunchedEffect(Unit) {
onLoadFinish?.invoke()
}
)
}
// 2. 文字叠加层:当没有书籍自身封面(或加载失败)时,在随机底图上绘制书名/作者
if (!isOnlineCoverLoaded) {
CoverTextOverlay(
name = name,
@@ -155,7 +142,6 @@ fun BookCover(
)
}
// 3. 角标 (Badge)
if (!badgeText.isNullOrEmpty()) {
TextCard(
text = badgeText,
@@ -218,7 +204,6 @@ private fun CoverTextOverlay(
val textPaint = TextPaint(paint).apply {
textAlign = Paint.Align.LEFT
}
val layout = StaticLayout.Builder
.obtain(name, 0, name.length, textPaint, maxWidth)
.setAlignment(Layout.Alignment.ALIGN_CENTER)