refactor: 优化顶部应用栏动画速率 & 重构待办卡片样式

This commit is contained in:
Super12138 2026-02-09 08:21:13 +08:00
parent eb7fa59302
commit 6bdee1f2f7
12 changed files with 105 additions and 71 deletions

View file

@ -38,7 +38,7 @@ android {
applicationId = "cn.super12138.todo" applicationId = "cn.super12138.todo"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = 986 versionCode = 987
versionName = "2.3.3" versionName = "2.3.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

View file

@ -47,14 +47,17 @@ object TodoDefaults {
bottomEnd = ZeroCornerSize bottomEnd = ZeroCornerSize
)*/ )*/
val ContainerColor: Color object Colors {
@Composable val Container: Color
get() = MaterialTheme.colorScheme.surfaceBright @Composable
get() = MaterialTheme.colorScheme.surfaceBright
val BackgroundColor: Color val Background: Color
@Composable @Composable
get() = MaterialTheme.colorScheme.surfaceContainer get() = MaterialTheme.colorScheme.surfaceContainer
val Green = Color(0xFF349938)
}
val fadedEdgeWidth = 8.dp val fadedEdgeWidth = 8.dp

View file

@ -106,7 +106,7 @@ class MainActivity : ComponentActivity() {
dynamicColor = dynamicColor dynamicColor = dynamicColor
) { ) {
Surface( Surface(
color = TodoDefaults.BackgroundColor, color = TodoDefaults.Colors.Background,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
val view = LocalView.current val view = LocalView.current
@ -140,7 +140,7 @@ class MainActivity : ComponentActivity() {
) )
} }
}, },
containerColor = TodoDefaults.BackgroundColor, containerColor = TodoDefaults.Colors.Background,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
TopNavigation( TopNavigation(

View file

@ -130,7 +130,7 @@ fun TopAppBarScaffold(
navigationIcon = navigationIcon, navigationIcon = navigationIcon,
actions = actions, actions = actions,
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = TodoDefaults.BackgroundColor, containerColor = TodoDefaults.Colors.Background,
) )
) )
}, },
@ -151,7 +151,7 @@ fun TopAppBarScaffold(
floatingActionButton: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End, floatingActionButtonPosition: FabPosition = FabPosition.End,
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
containerColor: Color = TodoDefaults.BackgroundColor, containerColor: Color = TodoDefaults.Colors.Background,
screenShape: Shape = TodoDefaults.ScreenContainerShape, screenShape: Shape = TodoDefaults.ScreenContainerShape,
content: @Composable () -> Unit content: @Composable () -> Unit
) = Scaffold( ) = Scaffold(

View file

@ -16,7 +16,7 @@ fun RoundedScreenContainer(
) { ) {
Surface( Surface(
modifier = modifier.padding(horizontal = TodoDefaults.screenHorizontalPadding), modifier = modifier.padding(horizontal = TodoDefaults.screenHorizontalPadding),
color = TodoDefaults.BackgroundColor, color = TodoDefaults.Colors.Background,
shape = TodoDefaults.ScreenContainerShape, shape = TodoDefaults.ScreenContainerShape,
content = content content = content
) )

View file

@ -41,7 +41,7 @@ fun RoundedCornerCardLarge(
@DrawableRes iconRes: Int, @DrawableRes iconRes: Int,
title: String, title: String,
count: Int, count: Int,
containerColor: Color = TodoDefaults.ContainerColor, containerColor: Color = TodoDefaults.Colors.Container,
shapes: ButtonShapes = TodoDefaults.shapes(), shapes: ButtonShapes = TodoDefaults.shapes(),
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {

View file

@ -36,7 +36,7 @@ import cn.super12138.todo.utils.toRelativeTimeString
fun UpcomingTaskCard( fun UpcomingTaskCard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
nextWeekTodo: List<TodoEntity>, nextWeekTodo: List<TodoEntity>,
containerColor: Color = TodoDefaults.ContainerColor containerColor: Color = TodoDefaults.Colors.Container
) { ) {
Card( Card(
modifier = modifier.height(TodoDefaults.overviewCardHeight * 2), modifier = modifier.height(TodoDefaults.overviewCardHeight * 2),

View file

@ -37,12 +37,12 @@ fun RowSettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = TodoDefaults.ContainerColor, background: Color = TodoDefaults.Colors.Container,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
verticalAlignment: Alignment.Vertical = Alignment.Top, verticalAlignment: Alignment.Vertical = Alignment.Top,
scrollState: ScrollState = rememberScrollState(), scrollState: ScrollState = rememberScrollState(),
fadedEdgeWidth: Dp, fadedEdgeWidth: Dp,
maskColor: Color = TodoDefaults.ContainerColor, maskColor: Color = TodoDefaults.Colors.Container,
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
MoreContentSettingsItem( MoreContentSettingsItem(
@ -88,11 +88,11 @@ fun LazyRowSettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = TodoDefaults.ContainerColor, background: Color = TodoDefaults.Colors.Container,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
verticalAlignment: Alignment.Vertical = Alignment.Top, verticalAlignment: Alignment.Vertical = Alignment.Top,
fadedEdgeWidth: Dp, fadedEdgeWidth: Dp,
maskColor: Color = TodoDefaults.ContainerColor, maskColor: Color = TodoDefaults.Colors.Container,
content: LazyListScope.() -> Unit content: LazyListScope.() -> Unit
) { ) {
MoreContentSettingsItem( MoreContentSettingsItem(
@ -137,7 +137,7 @@ fun MoreContentSettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = TodoDefaults.ContainerColor, background: Color = TodoDefaults.Colors.Container,
shape: CornerBasedShape = TodoDefaults.defaultShape, shape: CornerBasedShape = TodoDefaults.defaultShape,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {

View file

@ -61,7 +61,7 @@ fun SettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = TodoDefaults.ContainerColor, background: Color = TodoDefaults.Colors.Container,
shapes: ButtonShapes = TodoDefaults.shapes(), shapes: ButtonShapes = TodoDefaults.shapes(),
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
@ -125,7 +125,7 @@ fun SettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = TodoDefaults.ContainerColor, background: Color = TodoDefaults.Colors.Container,
shapes: ButtonShapes = TodoDefaults.shapes(), shapes: ButtonShapes = TodoDefaults.shapes(),
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
@ -159,7 +159,7 @@ fun SettingsItem(
title: String, title: String,
description: String? = null, description: String? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = TodoDefaults.ContainerColor, background: Color = TodoDefaults.Colors.Container,
shapes: ButtonShapes = TodoDefaults.shapes(), shapes: ButtonShapes = TodoDefaults.shapes(),
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {}, onClick: () -> Unit = {},
@ -204,7 +204,7 @@ fun SettingsItem(
headlineContent: (@Composable () -> Unit)? = null, headlineContent: (@Composable () -> Unit)? = null,
supportingContent: (@Composable () -> Unit)? = null, supportingContent: (@Composable () -> Unit)? = null,
trailingContent: (@Composable () -> Unit)? = null, trailingContent: (@Composable () -> Unit)? = null,
background: Color = TodoDefaults.ContainerColor, background: Color = TodoDefaults.Colors.Container,
shapes: ButtonShapes = TodoDefaults.shapes(), shapes: ButtonShapes = TodoDefaults.shapes(),
interactionSource: MutableInteractionSource? = null, interactionSource: MutableInteractionSource? = null,
enableClick: Boolean = true, enableClick: Boolean = true,

View file

@ -126,6 +126,7 @@ fun SharedTransitionScope.TasksPage(
content = task.content, content = task.content,
category = task.category, category = task.category,
completed = task.isCompleted, completed = task.isCompleted,
dueDate = task.dueDate,
priority = Priority.fromFloat(task.priority), priority = Priority.fromFloat(task.priority),
selected = selectedTodoIds.contains(task.id), selected = selectedTodoIds.contains(task.id),
onCardClick = { onCardClick = {

View file

@ -7,6 +7,7 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkHorizontally import androidx.compose.animation.shrinkHorizontally
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.interaction.collectIsPressedAsState
@ -14,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.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@ -24,7 +26,6 @@ import androidx.compose.material3.ButtonShapes
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -37,9 +38,12 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
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 cn.super12138.todo.R import cn.super12138.todo.R
@ -50,6 +54,8 @@ import cn.super12138.todo.utils.VibrationUtils
import cn.super12138.todo.utils.containerColor import cn.super12138.todo.utils.containerColor
import cn.super12138.todo.utils.disabledContainerColor import cn.super12138.todo.utils.disabledContainerColor
import cn.super12138.todo.utils.disabledContentColor import cn.super12138.todo.utils.disabledContentColor
import cn.super12138.todo.utils.toLocalDateString
import cn.super12138.todo.utils.toRelativeTimeString
@OptIn(ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
@ -59,6 +65,7 @@ fun TodoCard(
content: String, content: String,
category: String, category: String,
completed: Boolean, completed: Boolean,
dueDate: Long?,
priority: Priority, priority: Priority,
selected: Boolean, selected: Boolean,
onCardClick: () -> Unit = {}, onCardClick: () -> Unit = {},
@ -67,8 +74,9 @@ fun TodoCard(
shapes: ButtonShapes = TodoDefaults.shapes(), shapes: ButtonShapes = TodoDefaults.shapes(),
) { ) {
val view = LocalView.current val view = LocalView.current
val context = LocalContext.current
// TODO: 滑动删除 // TODO: 滑动删除
val cardColors = CardDefaults.cardColors(containerColor = TodoDefaults.ContainerColor) val cardColors = CardDefaults.cardColors(containerColor = TodoDefaults.Colors.Container)
val animatedContainerColor by animateColorAsState(targetValue = if (selected) MaterialTheme.colorScheme.secondaryContainer else if (completed) cardColors.disabledContainerColor else cardColors.containerColor) val animatedContainerColor by animateColorAsState(targetValue = if (selected) MaterialTheme.colorScheme.secondaryContainer else if (completed) cardColors.disabledContainerColor else cardColors.containerColor)
val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() }
@ -79,6 +87,13 @@ fun TodoCard(
animationSpec = TodoDefaults.shapesDefaultAnimationSpec animationSpec = TodoDefaults.shapesDefaultAnimationSpec
) )
val enterTransition = fadeIn(MaterialTheme.motionScheme.fastSpatialSpec()) + expandHorizontally(
MaterialTheme.motionScheme.fastSpatialSpec()
)
val exitTransition = fadeOut(MaterialTheme.motionScheme.fastSpatialSpec()) + shrinkHorizontally(
MaterialTheme.motionScheme.fastSpatialSpec()
)
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = modifier modifier = modifier
@ -98,16 +113,12 @@ fun TodoCard(
.drawBehind { .drawBehind {
drawRect(animatedContainerColor) drawRect(animatedContainerColor)
} }
.padding(horizontal = TodoDefaults.screenHorizontalPadding) .padding(start = TodoDefaults.screenHorizontalPadding)
) { ) {
AnimatedVisibility( AnimatedVisibility(
visible = selected, visible = selected,
enter = fadeIn(MaterialTheme.motionScheme.fastSpatialSpec()) + expandHorizontally( enter = enterTransition,
MaterialTheme.motionScheme.fastSpatialSpec() exit = exitTransition
),
exit = fadeOut(MaterialTheme.motionScheme.fastSpatialSpec()) + shrinkHorizontally(
MaterialTheme.motionScheme.fastSpatialSpec()
)
) { ) {
Box( Box(
Modifier Modifier
@ -125,10 +136,7 @@ fun TodoCard(
} }
Column( Column(
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.Center,
space = 5.dp,
alignment = Alignment.CenterVertically
),
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.fillMaxSize() .fillMaxSize()
@ -136,13 +144,42 @@ fun TodoCard(
CompositionLocalProvider( CompositionLocalProvider(
LocalContentColor provides if (completed) cardColors.disabledContentColor else cardColors.contentColor, LocalContentColor provides if (completed) cardColors.disabledContentColor else cardColors.contentColor,
) { ) {
Text( Row(
text = content, verticalAlignment = Alignment.Top,
style = MaterialTheme.typography.titleLarge, horizontalArrangement = Arrangement.SpaceBetween,
maxLines = 1, modifier = Modifier.fillMaxWidth()
overflow = TextOverflow.Ellipsis, ) {
// modifier = Modifier.basicMarquee() Text(
) text = content,
style = MaterialTheme.typography.titleLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
)
Column(
horizontalAlignment = Alignment.End,
modifier = Modifier.padding(
start = TodoDefaults.screenVerticalPadding,
end = TodoDefaults.screenHorizontalPadding
)
) {
Text(
text = dueDate.toLocalDateString(),
style = MaterialTheme.typography.labelLarge.copy(
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.Bold
)
)
Text(
text = dueDate.toRelativeTimeString(context),
style = MaterialTheme.typography.labelSmall.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant
)
)
}
}
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -166,35 +203,28 @@ fun TodoCard(
} }
} }
AnimatedVisibility(!selected && !completed) { AnimatedVisibility(
IconButton( visible = !selected && !completed,
onClick = { enter = enterTransition,
VibrationUtils.performHapticFeedback(view) exit = exitTransition
onChecked() ) {
} Box(
) {
Icon(
painter = painterResource(R.drawable.ic_check),
tint = MaterialTheme.colorScheme.primary,
contentDescription = stringResource(R.string.tip_mark_completed)
)
}
/*Box(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
modifier = Modifier modifier = Modifier
.width(50.dp)
.fillMaxHeight() .fillMaxHeight()
.background(MaterialTheme.colorScheme.tertiaryContainer) .background(TodoDefaults.Colors.Green)
.clickable { .clickable {
VibrationUtils.performHapticFeedback(view)
onChecked() onChecked()
} }
) { ) {
Icon( Icon(
imageVector = Icons.Outlined.Check, painter = painterResource(R.drawable.ic_check),
tint = MaterialTheme.colorScheme.primary, tint = Color.White,
contentDescription = "" contentDescription = null,
modifier = Modifier.padding(TodoDefaults.screenHorizontalPadding)
) )
}*/ }
} }
} }
} }

View file

@ -46,23 +46,23 @@ fun TodoTopAppBar(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val navIconEnterTransition = fadeIn( val navIconEnterTransition = fadeIn(
animationSpec = MaterialTheme.motionScheme.defaultEffectsSpec() animationSpec = MaterialTheme.motionScheme.fastEffectsSpec()
) + expandIn( ) + expandIn(
animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec(), animationSpec = MaterialTheme.motionScheme.fastSpatialSpec(),
expandFrom = Alignment.CenterStart expandFrom = Alignment.CenterStart
) )
val navIconExitTransition = fadeOut( val navIconExitTransition = fadeOut(
animationSpec = MaterialTheme.motionScheme.defaultEffectsSpec() animationSpec = MaterialTheme.motionScheme.fastEffectsSpec()
) + shrinkOut( ) + shrinkOut(
animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec(), animationSpec = MaterialTheme.motionScheme.fastSpatialSpec(),
shrinkTowards = Alignment.CenterStart shrinkTowards = Alignment.CenterStart
) )
val enterTransition = fadeIn(MaterialTheme.motionScheme.defaultEffectsSpec()) + val enterTransition = fadeIn(MaterialTheme.motionScheme.fastEffectsSpec()) +
scaleIn( scaleIn(
animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec(), animationSpec = MaterialTheme.motionScheme.fastSpatialSpec(),
initialScale = 0.92f initialScale = 0.92f
) )
val exitTransition = fadeOut(MaterialTheme.motionScheme.defaultEffectsSpec()) val exitTransition = fadeOut(MaterialTheme.motionScheme.fastEffectsSpec())
val titleTransition = ContentTransform( val titleTransition = ContentTransform(
targetContentEnter = enterTransition, targetContentEnter = enterTransition,
initialContentExit = exitTransition initialContentExit = exitTransition
@ -74,7 +74,7 @@ fun TodoTopAppBar(
targetValue = if (selectedMode) { targetValue = if (selectedMode) {
MaterialTheme.colorScheme.surfaceContainerHighest MaterialTheme.colorScheme.surfaceContainerHighest
} else { } else {
TodoDefaults.BackgroundColor TodoDefaults.Colors.Background
} }
) )