[优化] 优化发现界面,增加ModalBottomSheet的背景模糊效果。注意,仅为初始版本,存在不稳定情况。
This commit is contained in:
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) {
|
||||||
|
|||||||
Reference in new issue
Block a user