[优化] 增加控件

This commit is contained in:
HapeLee
2026-03-31 07:55:21 +08:00
parent 01fb34a1bd
commit 733312fe23
14 changed files with 206 additions and 117 deletions
@@ -295,7 +295,6 @@ fun ExploreShowScreen(
state = hazeState
),
title = selectedTitle ?: title,
colors = GlassTopAppBarDefaults.glassColors(),
navigationIcon = {
TopbarNavigationButton(onClick = onBack)
},
@@ -137,23 +137,28 @@ fun ThemeConfigScreen(
.verticalScroll(rememberScrollState())
.padding(16.dp)
) {
val composeEngine = remember { ThemeConfig.composeEngine }
val isMiuixEngine =
remember(composeEngine) { ThemeResolver.isMiuixEngine(composeEngine) }
val isDarkTheme = when (selectedThemeMode) {
"1" -> false
"2" -> true
else -> isSystemInDarkTheme()
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
ThemeCard(
context = context,
value = selectedTheme,
isDark = isDarkTheme,
isAmoled = ThemeConfig.isPureBlack,
paletteStyle = ThemeConfig.paletteStyle
)
if (!isMiuixEngine) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
ThemeCard(
context = context,
value = selectedTheme,
isDark = isDarkTheme,
isAmoled = ThemeConfig.isPureBlack,
paletteStyle = ThemeConfig.paletteStyle
)
}
}
Spacer(modifier = Modifier.height(16.dp))
@@ -165,46 +170,62 @@ fun ThemeConfigScreen(
}
SplicedColumnGroup(title = stringResource(R.string.theme)) {
ThemeModeSelector(
selectedMode = selectedThemeMode,
onModeSelected = { mode ->
selectedThemeMode = mode
ThemeConfig.themeMode = mode
OldThemeConfig.applyDayNight(context)
}
)
if (isMiuixEngine) {
DropdownListSettingItem(
title = stringResource(R.string.theme_mode),
selectedValue = selectedThemeMode,
displayEntries = stringArrayResource(R.array.theme_mode),
entryValues = stringArrayResource(R.array.theme_mode_v),
onValueChange = { mode ->
selectedThemeMode = mode
ThemeConfig.themeMode = mode
OldThemeConfig.applyDayNight(context)
}
)
} else {
ThemeModeSelector(
selectedMode = selectedThemeMode,
onModeSelected = { mode ->
selectedThemeMode = mode
ThemeConfig.themeMode = mode
OldThemeConfig.applyDayNight(context)
}
)
}
Spacer(modifier = Modifier.height(16.dp))
if (!isMiuixEngine) {
Spacer(modifier = Modifier.height(16.dp))
ThemeColorSelector(
context = context,
themes = themes,
selectedTheme = selectedTheme,
isDark = isDarkTheme,
isAmoled = ThemeConfig.isPureBlack,
paletteStyle = ThemeConfig.paletteStyle,
onThemeSelected = { theme ->
if (theme == "13") {
val hasLightBg = !ThemeConfig.bgImageLight.isNullOrEmpty()
val hasDarkBg = !ThemeConfig.bgImageDark.isNullOrEmpty()
if (!hasLightBg || !hasDarkBg) {
context.toastOnUi(R.string.transparent_theme_alarm)
return@ThemeColorSelector
ThemeColorSelector(
context = context,
themes = themes,
selectedTheme = selectedTheme,
isDark = isDarkTheme,
isAmoled = ThemeConfig.isPureBlack,
paletteStyle = ThemeConfig.paletteStyle,
onThemeSelected = { theme ->
if (theme == "13") {
val hasLightBg = !ThemeConfig.bgImageLight.isNullOrEmpty()
val hasDarkBg = !ThemeConfig.bgImageDark.isNullOrEmpty()
if (!hasLightBg || !hasDarkBg) {
context.toastOnUi(R.string.transparent_theme_alarm)
return@ThemeColorSelector
} else {
AppConfig.containerOpacity = 0
}
}
val oldTheme = selectedTheme
selectedTheme = theme
ThemeConfig.appTheme = theme
val isDynamicSwitch = (oldTheme == "12" || theme == "12")
if (isDynamicSwitch) {
showRestartDialog = true
} else {
AppConfig.containerOpacity = 0
postEvent(EventBus.RECREATE, "")
}
}
val oldTheme = selectedTheme
selectedTheme = theme
ThemeConfig.appTheme = theme
val isDynamicSwitch = (oldTheme == "12" || theme == "12")
if (isDynamicSwitch) {
showRestartDialog = true
} else {
postEvent(EventBus.RECREATE, "")
}
}
)
)
}
}
SplicedColumnGroup {
@@ -240,7 +261,7 @@ fun ThemeConfigScreen(
R.string.font_scale_summary,
AppContextWrapper.getFontScale(context)
),
value = fontScaleValue.value,
value = fontScaleValue.floatValue,
defaultValue = 10f,
valueRange = 8f..16f,
steps = 7,
@@ -254,7 +254,11 @@ fun MainScreen(
labelString = stringResource(destination.labelId),
miuixIcon = if (selected) destination.miuixSelectedIcon else destination.miuixIcon,
m3Icon = {
if (selected) destination.m3SelectedIcon else destination.m3Icon
NavigationIcon(
destination = destination,
selected = selected,
upBooksCount = uiState.upBooksCount
)
},
m3IndicatorColor = GlassDefaults.glassColor(
noBlurColor = MaterialTheme.colorScheme.secondaryContainer,
@@ -42,7 +42,6 @@ import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -142,6 +141,8 @@ fun ExploreScreen(
}
}
val composeEngine = ThemeResolver.isMiuixEngine(composeEngine)
ListScaffold(
title = stringResource(R.string.discovery),
state = uiState,
@@ -201,7 +202,8 @@ fun ExploreScreen(
}
},
onRefresh = { viewModel.refreshExploreKinds(item) },
onDelete = { sourceToDelete = item }
onDelete = { sourceToDelete = item },
isMiuix = composeEngine
)
}
@@ -232,7 +234,8 @@ fun ExploreScreen(
putExtra("exploreUrl", kind.url)
}
}
}
},
isMiuix = composeEngine
)
}
@@ -268,7 +271,8 @@ fun ExploreScreen(
uiState.items.indexOfFirst { it.bookSourceUrl == item.bookSourceUrl }
if (index >= 0) listState.animateScrollToItem(index)
}
}
},
isMiuix = composeEngine
)
}
}
@@ -345,25 +349,25 @@ fun ExploreSourceHeader(
onLogin: () -> Unit,
onRefresh: () -> Unit,
onDelete: () -> Unit,
isMiuix: Boolean,
) {
var showMenu by remember { mutableStateOf(false) }
val rotation by animateFloatAsState(if (isExpanded) 90f else 0f, label = "rotation")
val composeEngine = ThemeResolver.isMiuixEngine(composeEngine)
val containerColor by animateColorAsState(
targetValue = if (isExpanded)
if (composeEngine) MiuixTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.secondaryContainer
if (isMiuix) MiuixTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.secondaryContainer
else
if (composeEngine) MiuixTheme.colorScheme.surfaceContainer else MaterialTheme.colorScheme.surfaceContainerLow,
if (isMiuix) MiuixTheme.colorScheme.surfaceContainer else MaterialTheme.colorScheme.surfaceContainerLow,
animationSpec = tween(durationMillis = 200, easing = FastOutSlowInEasing),
label = "CardColor"
)
val contentColor by animateColorAsState(
targetValue = if (isExpanded)
if (composeEngine) MiuixTheme.colorScheme.primary else MaterialTheme.colorScheme.primary
if (isMiuix) MiuixTheme.colorScheme.primary else MaterialTheme.colorScheme.primary
else
if (composeEngine) MiuixTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurface,
if (isMiuix) MiuixTheme.colorScheme.onSurface else MaterialTheme.colorScheme.onSurface,
animationSpec = tween(durationMillis = 200, easing = FastOutSlowInEasing),
label = "CardColor"
)
@@ -461,11 +465,11 @@ fun ExploreSourceHeader(
@Composable
fun ExploreStickyCard(
item: BookSourcePart,
onClick: () -> Unit
onClick: () -> Unit,
isMiuix: Boolean,
) {
TextCard(
text = item.bookSourceName,
backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh,
textStyle = LegadoTheme.typography.labelLarge,
horizontalPadding = 8.dp,
verticalPadding = 6.dp,
@@ -478,8 +482,29 @@ fun ExploreKindItem(
kind: ExploreKind,
isClickable: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
isMiuix: Boolean,
) {
val color = if (isMiuix)
MiuixTheme.colorScheme.surfaceContainer
else
MaterialTheme.colorScheme.secondaryContainer
val contentColor = if (isMiuix)
MiuixTheme.colorScheme.onSurface
else
MaterialTheme.colorScheme.secondary
val unClickBackColor = if (isMiuix)
MiuixTheme.colorScheme.surfaceContainer
else
MaterialTheme.colorScheme.surface
val unClickColor = if (isMiuix)
MiuixTheme.colorScheme.disabledOnSurface
else
MaterialTheme.colorScheme.primary
CompositionLocalProvider(
LocalMinimumInteractiveComponentSize provides Dp.Unspecified
) {
@@ -490,19 +515,19 @@ fun ExploreKindItem(
GlassCard(
onClick = onClick,
shape = shape,
containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.6f),
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
containerColor = color.copy(alpha = 0.6f),
contentColor = contentColor,
modifier = modifier
) {
KindText(kind)
}
} else {
OutlinedCard(
GlassCard(
shape = shape,
colors = CardDefaults.outlinedCardColors(
contentColor = MaterialTheme.colorScheme.primary
),
modifier = modifier
containerColor = unClickBackColor.copy(alpha = 0.6f),
contentColor = unClickColor,
modifier = modifier,
border = CardDefaults.outlinedCardBorder()
) {
KindText(kind)
}
@@ -1,6 +1,6 @@
package io.legado.app.ui.widget.components.icon
package io.legado.app.ui.widget.components
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -8,18 +8,20 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import io.legado.app.ui.theme.LegadoTheme.composeEngine
import io.legado.app.ui.theme.ThemeResolver
import top.yukonga.miuix.kmp.theme.LocalContentColor
import androidx.compose.material3.LocalContentColor as MaterialLocalContentColor
import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
import top.yukonga.miuix.kmp.theme.LocalContentColor as MiuixLocalContentColor
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun AppIcon(
imageVector: ImageVector,
contentDescription: String?,
modifier: Modifier = Modifier,
tint: Color = LocalContentColor.current,
tint: Color = currentContentColor(),
) {
if (ThemeResolver.isMiuixEngine(composeEngine)) {
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
if (isMiuix) {
MiuixIcon(
imageVector = imageVector,
contentDescription = contentDescription,
@@ -34,4 +36,14 @@ fun AppIcon(
tint = tint
)
}
}
@Composable
fun currentContentColor(): Color {
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
return if (isMiuix) {
MiuixLocalContentColor.current
} else {
MaterialLocalContentColor.current
}
}
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.material3.contentColorFor
@@ -35,7 +34,7 @@ fun AppScaffold(
snackbarHost: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
contentColor: Color = contentColorFor(MaterialTheme.colorScheme.background),
contentColor: Color = contentColorFor(MiuixTheme.colorScheme.surface),
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
content: @Composable (PaddingValues) -> Unit
) {
@@ -56,7 +55,9 @@ fun AppScaffold(
MiuixTheme.colorScheme.surface
}
CompositionLocalProvider(LocalHazeState provides hazeState) {
CompositionLocalProvider(
LocalHazeState provides if (ThemeConfig.enableBlur) hazeState else null
) {
when {
ThemeResolver.isMiuixEngine(composeEngine) -> {
val miuixFabPosition = when (floatingActionButtonPosition) {
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -18,7 +17,11 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.LegadoTheme.composeEngine
import io.legado.app.ui.theme.ThemeResolver
import io.legado.app.ui.widget.components.AnimatedTextLine
import io.legado.app.ui.widget.components.AppIcon
import top.yukonga.miuix.kmp.theme.MiuixTheme
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -28,8 +31,8 @@ fun TextCard(
text: String,
icon: ImageVector? = null,
onClick: (() -> Unit)? = null,
backgroundColor: Color = MaterialTheme.colorScheme.primaryContainer,
contentColor: Color = MaterialTheme.colorScheme.onPrimaryContainer,
backgroundColor: Color? = null,
contentColor: Color? = null,
cornerRadius: Dp = 8.dp,
horizontalPadding: Dp = 8.dp,
verticalPadding: Dp = 2.dp,
@@ -37,11 +40,26 @@ fun TextCard(
spacing: Dp = 4.dp,
textStyle: TextStyle = LegadoTheme.typography.labelSmallEmphasized
) {
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
val defaultBackground = if (isMiuix)
MiuixTheme.colorScheme.surfaceContainer
else
MaterialTheme.colorScheme.primaryContainer
val defaultContent = if (isMiuix)
MiuixTheme.colorScheme.onSurface
else
MaterialTheme.colorScheme.primary
val finalBackgroundColor = backgroundColor ?: defaultBackground
val finalContentColor = contentColor ?: defaultContent
NormalCard(
modifier = modifier,
shape = RoundedCornerShape(cornerRadius),
containerColor = backgroundColor,
contentColor = contentColor,
containerColor = finalBackgroundColor,
contentColor = finalContentColor,
onClick = onClick
) {
Row(
@@ -53,10 +71,10 @@ fun TextCard(
) {
if (icon != null) {
Icon(
AppIcon(
imageVector = icon,
contentDescription = null,
tint = contentColor,
tint = finalContentColor,
modifier = Modifier.size(iconSize)
)
Spacer(modifier = Modifier.width(spacing))
@@ -65,7 +83,7 @@ fun TextCard(
AnimatedTextLine(
text = text,
style = textStyle,
color = contentColor
color = finalContentColor
)
}
}
@@ -5,7 +5,6 @@ import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.RowScope
@@ -17,16 +16,12 @@ import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.graphics.vector.ImageVector
import io.legado.app.ui.widget.components.AnimatedTextLine
import io.legado.app.ui.widget.components.SearchBarSection
@@ -58,19 +53,10 @@ fun <T> DynamicTopAppBar(
var showMenu by remember { mutableStateOf(false) }
val isSelecting = state.selectedIds.isNotEmpty()
val containerColor = GlassTopAppBarDefaults.containerColor()
val scrolledColor = GlassTopAppBarDefaults.scrolledContainerColor()
val animatedColor by remember(scrollBehavior, containerColor, scrolledColor) {
derivedStateOf {
lerp(containerColor, scrolledColor, scrollBehavior.collapsedFraction)
}
}
GlassMediumFlexibleTopAppBar(
modifier = Modifier
.fillMaxWidth()
.background(animatedColor),
.fillMaxWidth(),
title = when {
state.isLoading -> "请稍后..."
isSelecting -> "已选择 ${state.selectedIds.size}/${state.items.size}"
@@ -117,10 +103,6 @@ fun <T> DynamicTopAppBar(
}
},
scrollBehavior = scrollBehavior,
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent
),
bottomContent = {
AnimatedVisibility(
visible = state.isSearch && !isSelecting,
@@ -1,5 +1,6 @@
package io.legado.app.ui.widget.components.topbar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.RowScope
@@ -13,6 +14,7 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.text.style.TextOverflow
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.theme.LegadoTheme
@@ -35,28 +37,54 @@ fun GlassMediumFlexibleTopAppBar(
scrollBehavior: GlassTopAppBarScrollBehavior? = null,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
colors: TopAppBarColors = GlassTopAppBarDefaults.glassColors(),
bottomContent: @Composable (ColumnScope.() -> Unit)? = null
) {
val hazeState = LocalHazeState.current
val composeEngine = LegadoTheme.composeEngine
val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
val containerColor = if (!isMiuix) {
GlassTopAppBarDefaults.containerColor()
} else {
GlassTopAppBarDefaults.getMiuixAppBarColor()
}
val scrolledColor = if (!isMiuix) {
GlassTopAppBarDefaults.scrolledContainerColor()
} else {
GlassTopAppBarDefaults.getMiuixAppBarColor()
}
val animatedColor = if (!isMiuix) {
val fraction = scrollBehavior?.collapsedFraction ?: 0f
lerp(containerColor, scrolledColor, fraction)
} else {
containerColor
}
val finalModifier = if (hazeState != null) {
modifier.responsiveHazeEffect(state = hazeState)
} else {
modifier
modifier.background(color = animatedColor)
}
val composeEngine = LegadoTheme.composeEngine
val transparentColors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent
)
Column(modifier = finalModifier) {
Column(
modifier = finalModifier
) {
when {
ThemeResolver.isMiuixEngine(composeEngine) -> {
isMiuix -> {
MiuixTopAppBar(
modifier = Modifier,
title = title,
navigationIcon = navigationIcon,
actions = actions,
color = GlassTopAppBarDefaults.getMiuixAppBarColor(),
color = Color.Transparent, // 内部强制透明,透出 Column 背景
scrollBehavior = (scrollBehavior as? MiuixGlassScrollBehavior)?.miuixBehavior
)
}
@@ -77,7 +105,7 @@ fun GlassMediumFlexibleTopAppBar(
navigationIcon = navigationIcon,
actions = actions,
scrollBehavior = (scrollBehavior as? M3GlassScrollBehavior)?.m3Behavior,
colors = colors
colors = transparentColors // 应用透明背景
)
} else {
TopAppBar(
@@ -93,11 +121,13 @@ fun GlassMediumFlexibleTopAppBar(
navigationIcon = navigationIcon,
actions = actions,
scrollBehavior = (scrollBehavior as? M3GlassScrollBehavior)?.m3Behavior,
colors = colors
colors = transparentColors // 应用透明背景
)
}
}
}
// 现在的 bottomContent 完美包裹在外层 Column 内,共享渐变和模糊效果!
bottomContent?.invoke(this)
}
}
@@ -178,7 +178,6 @@
<string-array name="composeEngine">
<item>Material Design</item>
<item>SaltUI</item>
<item>Miuix</item>
</string-array>
+1 -1
View File
@@ -493,7 +493,7 @@
<string name="share_selected_source">分享选中源</string>
<string name="scan_qr_code">扫描二维码</string>
<string name="click_on_selected_show_menu">选中时点击可弹出菜单</string>
<string name="theme">主题</string>
<string name="theme">主题模式</string>
<string name="theme_mode">深色模式</string>
<string name="theme_mode_desc">选择主题模式</string>
<string name="join_qq_group">加入 QQ 群</string>
-1
View File
@@ -105,7 +105,6 @@
<string-array name="composeEngine_value">
<item>material</item>
<item>salt</item>
<item>miuix</item>
</string-array>
-1
View File
@@ -219,7 +219,6 @@
<string-array name="composeEngine">
<item>Material Design</item>
<item>SaltUI</item>
<item>Miuix</item>
</string-array>
+1 -1
View File
@@ -1,5 +1,5 @@
VERSION_MAJOR=3
VERSION_MINOR=26
VERSION_PATCH=10
VERSION_PATCH=11
VERSION_SUFFIX=1
# 1 = Pre, 0 = Release