[优化] 优化发现界面,增加ModalBottomSheet的背景模糊效果。注意,仅为初始版本,存在不稳定情况。

This commit is contained in:
HapeLee committed 2026-01-08 02:01:10 +08:00
1 parent 7173cbfbe2
commit c4345d4945
1 file changed
+34 -14
@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.FlowRowScope
import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -86,6 +87,12 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import dev.chrisbanes.haze.HazeProgressive
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeEffect
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
import dev.chrisbanes.haze.materials.HazeMaterials
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.rule.ExploreKind import io.legado.app.data.entities.rule.ExploreKind
import io.legado.app.model.BookShelfState import io.legado.app.model.BookShelfState
@@ -98,7 +105,7 @@ import kotlinx.coroutines.flow.Flow
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@SuppressLint("LocalContextConfigurationRead", "ConfigurationScreenWidthHeight") @SuppressLint("LocalContextConfigurationRead", "ConfigurationScreenWidthHeight")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalHazeMaterialsApi::class)
@Composable @Composable
fun ExploreShowScreen( fun ExploreShowScreen(
title: String, title: String,
@@ -128,7 +135,7 @@ fun ExploreShowScreen(
val isGridMode = layoutState == 1 val isGridMode = layoutState == 1
var showGridCountSheet by remember { mutableStateOf(false) } var showGridCountSheet by remember { mutableStateOf(false) }
val gridColumnCount by viewModel.gridCount.collectAsState() val gridColumnCount by viewModel.gridCount.collectAsState()
val hazeState = remember { HazeState() }
val shouldLoadMoreList = remember { val shouldLoadMoreList = remember {
derivedStateOf { derivedStateOf {
val total = listState.layoutInfo.totalItemsCount val total = listState.layoutInfo.totalItemsCount
@@ -178,12 +185,14 @@ fun ExploreShowScreen(
ModalBottomSheet( ModalBottomSheet(
sheetState = sheetState, sheetState = sheetState,
dragHandle = null,
onDismissRequest = { showGridCountSheet = false } onDismissRequest = { showGridCountSheet = false }
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(24.dp) .hazeEffect(state = hazeState, style = HazeMaterials.regular())
.padding(16.dp)
) { ) {
Text( Text(
@@ -217,7 +226,6 @@ fun ExploreShowScreen(
if (showKindSheet) { if (showKindSheet) {
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
var kindQuery by remember { mutableStateOf("") }
val sheetState = rememberModalBottomSheetState( val sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true, skipPartiallyExpanded = true,
confirmValueChange = { newValue -> confirmValueChange = { newValue ->
@@ -226,12 +234,14 @@ fun ExploreShowScreen(
) )
ModalBottomSheet( ModalBottomSheet(
sheetState = sheetState, sheetState = sheetState,
dragHandle = null,
onDismissRequest = { showKindSheet = false } onDismissRequest = { showKindSheet = false }
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.hazeEffect(state = hazeState, style = HazeMaterials.regular())
.fillMaxWidth() .fillMaxWidth()
.heightIn(max = LocalConfiguration.current.screenHeightDp.dp * 0.8f) .heightIn(max = LocalConfiguration.current.screenHeightDp.dp * 0.72f)
.verticalScroll(scrollState) .verticalScroll(scrollState)
) { ) {
@@ -255,7 +265,9 @@ fun ExploreShowScreen(
FlowRow( FlowRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp) .padding(16.dp),
maxItemsInEachRow = 3, // 强制每行最多 3 个
horizontalArrangement = Arrangement.spacedBy(0.dp) // 因为 Item 内部有 padding,这里设为 0
) { ) {
filteredKinds.forEach { kind -> filteredKinds.forEach { kind ->
KindListItem( KindListItem(
@@ -273,9 +285,15 @@ fun ExploreShowScreen(
} }
Scaffold( Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
.hazeSource(hazeState),
topBar = { topBar = {
ExploreTopBar( ExploreTopBar(
modifier = Modifier.hazeEffect(hazeState) {
progressive =
HazeProgressive.verticalGradient(startIntensity = 1f, endIntensity = 0f)
},
title = selectedTitle ?: title, title = selectedTitle ?: title,
filterState = filterState, filterState = filterState,
onBack = onBack, onBack = onBack,
@@ -293,7 +311,6 @@ fun ExploreShowScreen(
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues)
) { ) {
Crossfade( Crossfade(
targetState = isGridMode, targetState = isGridMode,
animationSpec = tween(250), animationSpec = tween(250),
@@ -338,7 +355,6 @@ fun ExploreShowScreen(
items = books, items = books,
key = { it.bookUrl } key = { it.bookUrl }
) { book -> ) { book ->
val shelfState = viewModel.getCurrentBookShelfState(book)
ExploreBookItem( ExploreBookItem(
book = book, book = book,
shelfState = viewModel.getBookShelfStateFlow(book), shelfState = viewModel.getBookShelfStateFlow(book),
@@ -364,13 +380,15 @@ fun ExploreShowScreen(
} }
@Composable @Composable
fun KindListItem( fun FlowRowScope.KindListItem(
kind: ExploreKind, kind: ExploreKind,
currentTitle: String?, currentTitle: String?,
onClick: () -> Unit onClick: () -> Unit,
modifier: Modifier = Modifier
) { ) {
val isClickable = !kind.url.isNullOrBlank() val isClickable = !kind.url.isNullOrBlank()
val isSelected = kind.title == currentTitle val isSelected = kind.title == currentTitle
FilterChip( FilterChip(
onClick = { if (isClickable) onClick() }, onClick = { if (isClickable) onClick() },
enabled = isClickable, enabled = isClickable,
@@ -385,18 +403,19 @@ fun KindListItem(
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
}, },
modifier = Modifier modifier = modifier
.padding(horizontal = 4.dp)
.then( .then(
if (!isClickable) Modifier.fillMaxWidth() if (!isClickable) Modifier.fillMaxWidth()
else Modifier.fillMaxWidth(1f / 3f) else Modifier.weight(1f)
) )
.padding(horizontal = 4.dp)
) )
} }
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun ExploreTopBar( fun ExploreTopBar(
modifier: Modifier = Modifier,
title: String, title: String,
filterState: BookFilterState, filterState: BookFilterState,
onBack: () -> Unit, onBack: () -> Unit,
@@ -410,6 +429,7 @@ fun ExploreTopBar(
var showMenu by remember { mutableStateOf(false) } var showMenu by remember { mutableStateOf(false) }
MediumFlexibleTopAppBar( MediumFlexibleTopAppBar(
modifier = modifier,
title = { AnimatedTextLine(title, maxLines = 1, overflow = TextOverflow.Ellipsis) }, title = { AnimatedTextLine(title, maxLines = 1, overflow = TextOverflow.Ellipsis) },
navigationIcon = { navigationIcon = {
IconButton(onClick = onBack) { IconButton(onClick = onBack) {