feat: 转场动画优化 & 任务界面 MD3E 适配 & 待办卡片样式更新
This commit is contained in:
parent
2b28b446eb
commit
5b87a17a82
8 changed files with 182 additions and 93 deletions
|
|
@ -38,7 +38,7 @@ android {
|
||||||
applicationId = "cn.super12138.todo"
|
applicationId = "cn.super12138.todo"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 950
|
versionCode = 951
|
||||||
versionName = "2.3.3"
|
versionName = "2.3.3"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ object TodoDefaults {
|
||||||
/**
|
/**
|
||||||
* 待办卡片默认高度
|
* 待办卡片默认高度
|
||||||
*/
|
*/
|
||||||
val toDoCardHeight = 80.dp
|
val toDoCardHeight = 86.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置项水平边距
|
* 设置项水平边距
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,6 @@ package cn.super12138.todo.ui.navigation
|
||||||
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.SharedTransitionLayout
|
import androidx.compose.animation.SharedTransitionLayout
|
||||||
import androidx.compose.animation.fadeIn
|
|
||||||
import androidx.compose.animation.fadeOut
|
|
||||||
import androidx.compose.animation.togetherWith
|
|
||||||
import androidx.compose.animation.unveilIn
|
|
||||||
import androidx.compose.animation.veilOut
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -17,8 +12,13 @@ import cn.super12138.todo.ui.pages.editor.TodoAddPage
|
||||||
import cn.super12138.todo.ui.pages.editor.TodoEditPage
|
import cn.super12138.todo.ui.pages.editor.TodoEditPage
|
||||||
import cn.super12138.todo.ui.pages.overview.OverviewPage
|
import cn.super12138.todo.ui.pages.overview.OverviewPage
|
||||||
import cn.super12138.todo.ui.pages.tasks.TasksPage
|
import cn.super12138.todo.ui.pages.tasks.TasksPage
|
||||||
|
import cn.super12138.todo.ui.theme.fadeThrough
|
||||||
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
import cn.super12138.todo.ui.viewmodels.MainViewModel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 来自:https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/transition/MaterialFadeThrough.java#L33
|
||||||
|
*/
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TopNavigation(
|
fun TopNavigation(
|
||||||
|
|
@ -53,12 +53,12 @@ fun TopNavigation(
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val veilColor = MaterialTheme.colorScheme.surfaceDim
|
// val veilColor = MaterialTheme.colorScheme.surfaceDim
|
||||||
SharedTransitionLayout {
|
SharedTransitionLayout {
|
||||||
NavDisplay(
|
NavDisplay(
|
||||||
backStack = backStack.backStack,
|
backStack = backStack.backStack,
|
||||||
onBack = ::onBack,
|
onBack = ::onBack,
|
||||||
transitionSpec = {
|
/*transitionSpec = {
|
||||||
fadeIn() togetherWith veilOut(targetColor = veilColor)
|
fadeIn() togetherWith veilOut(targetColor = veilColor)
|
||||||
},
|
},
|
||||||
popTransitionSpec = {
|
popTransitionSpec = {
|
||||||
|
|
@ -66,6 +66,15 @@ fun TopNavigation(
|
||||||
},
|
},
|
||||||
predictivePopTransitionSpec = {
|
predictivePopTransitionSpec = {
|
||||||
unveilIn(initialColor = veilColor) togetherWith fadeOut()
|
unveilIn(initialColor = veilColor) togetherWith fadeOut()
|
||||||
|
},*/
|
||||||
|
transitionSpec = {
|
||||||
|
fadeThrough()
|
||||||
|
},
|
||||||
|
popTransitionSpec = {
|
||||||
|
fadeThrough()
|
||||||
|
},
|
||||||
|
predictivePopTransitionSpec = {
|
||||||
|
fadeThrough()
|
||||||
},
|
},
|
||||||
entryProvider = entryProvider {
|
entryProvider = entryProvider {
|
||||||
entry<TodoScreen.Overview> {
|
entry<TodoScreen.Overview> {
|
||||||
|
|
|
||||||
|
|
@ -13,23 +13,20 @@ import androidx.compose.foundation.layout.Spacer
|
||||||
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.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Switch
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalView
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation3.ui.LocalNavAnimatedContentScope
|
import androidx.navigation3.ui.LocalNavAnimatedContentScope
|
||||||
|
|
@ -45,9 +42,9 @@ import cn.super12138.todo.ui.components.TopAppBarScaffold
|
||||||
import cn.super12138.todo.ui.pages.editor.components.TodoCategoryChip
|
import cn.super12138.todo.ui.pages.editor.components.TodoCategoryChip
|
||||||
import cn.super12138.todo.ui.pages.editor.components.TodoCategoryTextField
|
import cn.super12138.todo.ui.pages.editor.components.TodoCategoryTextField
|
||||||
import cn.super12138.todo.ui.pages.editor.components.TodoContentTextField
|
import cn.super12138.todo.ui.pages.editor.components.TodoContentTextField
|
||||||
|
import cn.super12138.todo.ui.pages.editor.components.TodoMarkAsCompletedCheckbox
|
||||||
import cn.super12138.todo.ui.pages.editor.components.TodoPrioritySlider
|
import cn.super12138.todo.ui.pages.editor.components.TodoPrioritySlider
|
||||||
import cn.super12138.todo.ui.pages.editor.state.rememberEditorState
|
import cn.super12138.todo.ui.pages.editor.state.rememberEditorState
|
||||||
import cn.super12138.todo.utils.VibrationUtils
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SharedTransitionScope.TodoAddPage(
|
fun SharedTransitionScope.TodoAddPage(
|
||||||
|
|
@ -92,8 +89,6 @@ private fun TodoEditorPage(
|
||||||
onNavigateUp: () -> Unit
|
onNavigateUp: () -> Unit
|
||||||
) {
|
) {
|
||||||
// TODO: 本页及其相关组件重组性能检查优化
|
// TODO: 本页及其相关组件重组性能检查优化
|
||||||
val view = LocalView.current
|
|
||||||
|
|
||||||
val uiState = rememberEditorState(initialTodo = toDo)
|
val uiState = rememberEditorState(initialTodo = toDo)
|
||||||
|
|
||||||
val originalCategories by DataStoreManager.categoriesFlow.collectAsState(initial = emptyList())
|
val originalCategories by DataStoreManager.categoriesFlow.collectAsState(initial = emptyList())
|
||||||
|
|
@ -180,11 +175,11 @@ private fun TodoEditorPage(
|
||||||
verticalArrangement = Arrangement.spacedBy(5.dp),
|
verticalArrangement = Arrangement.spacedBy(5.dp),
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
item {
|
item(key = 0) {
|
||||||
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item(key=1) {
|
||||||
TodoContentTextField(
|
TodoContentTextField(
|
||||||
value = uiState.toDoContent,
|
value = uiState.toDoContent,
|
||||||
onValueChange = { uiState.toDoContent = it },
|
onValueChange = { uiState.toDoContent = it },
|
||||||
|
|
@ -193,7 +188,7 @@ private fun TodoEditorPage(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item(key = 2) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.label_category),
|
text = stringResource(R.string.label_category),
|
||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
|
@ -222,7 +217,7 @@ private fun TodoEditorPage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item (key = 3){
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.label_priority),
|
text = stringResource(R.string.label_priority),
|
||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
|
@ -234,34 +229,22 @@ private fun TodoEditorPage(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item(key = 4) {
|
||||||
if (toDo != null) {
|
if (toDo != null) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.label_more),
|
text = stringResource(R.string.label_more),
|
||||||
style = MaterialTheme.typography.titleMedium
|
style = MaterialTheme.typography.titleMedium
|
||||||
)
|
)
|
||||||
|
|
||||||
Row(
|
TodoMarkAsCompletedCheckbox(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
checked = uiState.isCompleted,
|
||||||
|
onCheckedChange = { uiState.isCompleted = it },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
)
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.tip_mark_completed),
|
|
||||||
style = MaterialTheme.typography.labelLarge,
|
|
||||||
modifier = Modifier.padding(end = 10.dp)
|
|
||||||
)
|
|
||||||
Switch(
|
|
||||||
checked = uiState.isCompleted,
|
|
||||||
onCheckedChange = {
|
|
||||||
VibrationUtils.performHapticFeedback(view)
|
|
||||||
uiState.isCompleted = it
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item {
|
item (key = 5){
|
||||||
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
Spacer(modifier = Modifier.size(TodoDefaults.screenVerticalPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package cn.super12138.todo.ui.pages.editor.components
|
||||||
|
|
||||||
|
import android.view.HapticFeedbackConstants
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.selection.toggleable
|
||||||
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import cn.super12138.todo.R
|
||||||
|
import cn.super12138.todo.utils.VibrationUtils
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TodoMarkAsCompletedCheckbox(
|
||||||
|
checked: Boolean,
|
||||||
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val view = LocalView.current
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = modifier
|
||||||
|
.toggleable(
|
||||||
|
value = checked,
|
||||||
|
onValueChange = {
|
||||||
|
VibrationUtils.performHapticFeedback(view, HapticFeedbackConstants.LONG_PRESS)
|
||||||
|
onCheckedChange(it)
|
||||||
|
},
|
||||||
|
role = Role.Checkbox,
|
||||||
|
indication = null,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.tip_mark_completed),
|
||||||
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
)
|
||||||
|
|
||||||
|
Checkbox(
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = {
|
||||||
|
VibrationUtils.performHapticFeedback(view, HapticFeedbackConstants.LONG_PRESS)
|
||||||
|
onCheckedChange(it)
|
||||||
|
},
|
||||||
|
interactionSource = interactionSource
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -27,7 +27,6 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.navigation3.ui.LocalNavAnimatedContentScope
|
import androidx.navigation3.ui.LocalNavAnimatedContentScope
|
||||||
import cn.super12138.todo.R
|
import cn.super12138.todo.R
|
||||||
import cn.super12138.todo.constants.Constants
|
import cn.super12138.todo.constants.Constants
|
||||||
|
|
@ -103,7 +102,7 @@ fun SharedTransitionScope.TasksPage(
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
verticalArrangement = Arrangement.spacedBy(5.dp),
|
verticalArrangement = Arrangement.spacedBy(TodoDefaults.settingsItemPadding),
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
|
|
@ -120,32 +119,34 @@ fun SharedTransitionScope.TasksPage(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
items(
|
itemsIndexed(
|
||||||
items = filteredTodoList,
|
items = filteredTodoList,
|
||||||
key = { it.id }
|
key = { _, task -> task.id }
|
||||||
) {
|
) { index, task ->
|
||||||
TodoCard(
|
TodoCard(
|
||||||
// id = item.id,
|
// id = item.id,
|
||||||
content = it.content,
|
content = task.content,
|
||||||
category = it.category,
|
category = task.category,
|
||||||
completed = it.isCompleted,
|
completed = task.isCompleted,
|
||||||
priority = Priority.fromFloat(it.priority),
|
priority = Priority.fromFloat(task.priority),
|
||||||
selected = selectedTodoIds.contains(it.id),
|
selected = selectedTodoIds.contains(task.id),
|
||||||
onCardClick = {
|
onCardClick = {
|
||||||
if (inSelectedMode) {
|
if (inSelectedMode) {
|
||||||
viewModel.toggleTodoSelection(it)
|
viewModel.toggleTodoSelection(task)
|
||||||
} else {
|
} else {
|
||||||
toTodoEditPage(it)
|
toTodoEditPage(task)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCardLongClick = { viewModel.toggleTodoSelection(it) },
|
onCardLongClick = { viewModel.toggleTodoSelection(task) },
|
||||||
onChecked = {
|
onChecked = {
|
||||||
viewModel.updateTodo(it.copy(isCompleted = true))
|
viewModel.updateTodo(task.copy(isCompleted = true))
|
||||||
viewModel.playConfetti()
|
viewModel.playConfetti()
|
||||||
},
|
},
|
||||||
|
topRounded = index == 0,
|
||||||
|
bottomRounded = index == filteredTodoList.size - 1,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.sharedBounds(
|
.sharedBounds(
|
||||||
sharedContentState = rememberSharedContentState(key = "${Constants.KEY_TODO_ITEM_TRANSITION}_${it.id}"),
|
sharedContentState = rememberSharedContentState(key = "${Constants.KEY_TODO_ITEM_TRANSITION}_${task.id}"),
|
||||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
animatedVisibilityScope = LocalNavAnimatedContentScope.current
|
||||||
)
|
)
|
||||||
.animateItem(
|
.animateItem(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.super12138.todo.ui.pages.tasks.components
|
package cn.super12138.todo.ui.pages.tasks.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.animation.expandHorizontally
|
import androidx.compose.animation.expandHorizontally
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
|
@ -17,10 +18,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.CornerBasedShape
|
||||||
import androidx.compose.material3.Badge
|
import androidx.compose.material3.Badge
|
||||||
import androidx.compose.material3.BadgedBox
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
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.IconButton
|
||||||
|
|
@ -28,6 +27,7 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.contentColorFor
|
import androidx.compose.material3.contentColorFor
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
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
|
||||||
|
|
@ -42,6 +42,7 @@ import cn.super12138.todo.logic.model.Priority
|
||||||
import cn.super12138.todo.ui.TodoDefaults
|
import cn.super12138.todo.ui.TodoDefaults
|
||||||
import cn.super12138.todo.utils.VibrationUtils
|
import cn.super12138.todo.utils.VibrationUtils
|
||||||
import cn.super12138.todo.utils.containerColor
|
import cn.super12138.todo.utils.containerColor
|
||||||
|
import cn.super12138.todo.utils.getPartialRoundedShape
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -55,19 +56,22 @@ fun TodoCard(
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
onCardClick: () -> Unit = {},
|
onCardClick: () -> Unit = {},
|
||||||
onCardLongClick: () -> Unit = {},
|
onCardLongClick: () -> Unit = {},
|
||||||
onChecked: () -> Unit = {}
|
onChecked: () -> Unit = {},
|
||||||
|
shape: CornerBasedShape = TodoDefaults.SettingsItemDefaultShape,
|
||||||
|
roundedShape: CornerBasedShape = TodoDefaults.SettingsItemRoundedShape,
|
||||||
|
topRounded: Boolean = false,
|
||||||
|
bottomRounded: Boolean = false
|
||||||
) {
|
) {
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
Card(
|
// TODO: 滑动删除
|
||||||
colors = CardDefaults.cardColors(containerColor = TodoDefaults.ContainerColor),
|
val animatedContainerColor by animateColorAsState(if (selected) MaterialTheme.colorScheme.secondaryContainer else TodoDefaults.ContainerColor)
|
||||||
modifier = modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(TodoDefaults.toDoCardHeight)
|
|
||||||
) {
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxWidth()
|
||||||
|
.height(TodoDefaults.toDoCardHeight)
|
||||||
|
.clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape))
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
VibrationUtils.performHapticFeedback(view)
|
VibrationUtils.performHapticFeedback(view)
|
||||||
|
|
@ -77,7 +81,8 @@ fun TodoCard(
|
||||||
// 因为 combinedClickable 在更新的 Compose 里已经处理好了触感反馈
|
// 因为 combinedClickable 在更新的 Compose 里已经处理好了触感反馈
|
||||||
onLongClick = onCardLongClick
|
onLongClick = onCardLongClick
|
||||||
)
|
)
|
||||||
.padding(horizontal = 15.dp)
|
.background(animatedContainerColor)
|
||||||
|
.padding(horizontal = TodoDefaults.screenHorizontalPadding)
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = selected,
|
visible = selected,
|
||||||
|
|
@ -104,40 +109,42 @@ fun TodoCard(
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.spacedBy(
|
||||||
|
space = 5.dp,
|
||||||
|
alignment = Alignment.CenterVertically
|
||||||
|
),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
BadgedBox(
|
Text(
|
||||||
badge = {
|
text = content,
|
||||||
Badge(
|
style = MaterialTheme.typography.titleLarge,
|
||||||
containerColor = priority.containerColor(),
|
maxLines = 1,
|
||||||
modifier = Modifier.padding(start = 5.dp)
|
overflow = TextOverflow.Ellipsis,
|
||||||
) {
|
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
||||||
Text(
|
modifier = Modifier.basicMarquee() // TODO: 后续评估性能影响
|
||||||
text = stringResource(priority.nameRes),
|
)
|
||||||
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
|
||||||
)
|
Row(
|
||||||
}
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
}
|
horizontalArrangement = Arrangement.spacedBy(5.dp)
|
||||||
) {
|
) {
|
||||||
|
Badge(containerColor = MaterialTheme.colorScheme.primary) {
|
||||||
|
Text(
|
||||||
|
text = category.ifEmpty { stringResource(R.string.tip_default_category) },
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = content,
|
text = stringResource(priority.nameRes),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.labelMedium.copy(priority.containerColor()),
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
||||||
modifier = Modifier.basicMarquee() // TODO: 后续评估性能影响
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
|
||||||
text = category.ifEmpty { stringResource(R.string.tip_default_category) },
|
|
||||||
style = MaterialTheme.typography.labelMedium,
|
|
||||||
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
|
||||||
maxLines = 1
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(!selected && !completed) {
|
AnimatedVisibility(!selected && !completed) {
|
||||||
|
|
@ -172,7 +179,7 @@ fun TodoCard(
|
||||||
)
|
)
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.scaleIn
|
||||||
import androidx.compose.animation.slideInHorizontally
|
import androidx.compose.animation.slideInHorizontally
|
||||||
import androidx.compose.animation.slideOutHorizontally
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
|
|
||||||
|
|
@ -17,6 +18,8 @@ import androidx.compose.animation.slideOutHorizontally
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private const val ProgressThreshold = 0.35f
|
private const val ProgressThreshold = 0.35f
|
||||||
|
private const val DEFAULT_START_SCALE = 0.95f //0.92f
|
||||||
|
private const val THRESHOLD_ALPHA = 0.5f
|
||||||
|
|
||||||
private val Int.ForOutgoing: Int
|
private val Int.ForOutgoing: Int
|
||||||
get() = (this * ProgressThreshold).toInt()
|
get() = (this * ProgressThreshold).toInt()
|
||||||
|
|
@ -84,4 +87,31 @@ fun materialSharedAxisXOut(
|
||||||
delayMillis = 0,
|
delayMillis = 0,
|
||||||
easing = FastOutLinearInEasing
|
easing = FastOutLinearInEasing
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fadeThrough(
|
||||||
|
durationMillis: Int = 200,
|
||||||
|
): ContentTransform = ContentTransform(
|
||||||
|
fadeThroughIn(durationMillis = durationMillis),
|
||||||
|
fadeThroughOut(durationMillis = durationMillis)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fadeThroughIn(
|
||||||
|
durationMillis: Int = DefaultMotionDuration,
|
||||||
|
): EnterTransition =
|
||||||
|
fadeIn(
|
||||||
|
animationSpec = tween(durationMillis),
|
||||||
|
initialAlpha = THRESHOLD_ALPHA
|
||||||
|
) + scaleIn(
|
||||||
|
animationSpec = tween(durationMillis),
|
||||||
|
initialScale = DEFAULT_START_SCALE
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fadeThroughOut(
|
||||||
|
durationMillis: Int = DefaultMotionDuration,
|
||||||
|
): ExitTransition = fadeOut(
|
||||||
|
animationSpec = tween(
|
||||||
|
durationMillis = durationMillis,
|
||||||
|
easing = FastOutLinearInEasing
|
||||||
|
)
|
||||||
)
|
)
|
||||||
Loading…
Reference in a new issue