feat(main, editor): 更加流畅的待办内容动画
This commit is contained in:
parent
4d432935b3
commit
de27d5ddb5
6 changed files with 68 additions and 28 deletions
|
|
@ -5,6 +5,7 @@ object Constants {
|
||||||
const val GITHUB_REPO = "https://github.com/Super12138/ToDo/"
|
const val GITHUB_REPO = "https://github.com/Super12138/ToDo/"
|
||||||
|
|
||||||
const val KEY_TODO_FAB_TRANSITION = "todo_fab"
|
const val KEY_TODO_FAB_TRANSITION = "todo_fab"
|
||||||
|
const val KEY_TODO_CONTENT_TRANSITION = "todo_content"
|
||||||
|
|
||||||
const val SP_NAME = "cn.super12138.todo_preferences"
|
const val SP_NAME = "cn.super12138.todo_preferences"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ fun TodoNavigation(
|
||||||
toDo = viewModel.selectedEditTodo,
|
toDo = viewModel.selectedEditTodo,
|
||||||
onSave = {
|
onSave = {
|
||||||
viewModel.addTodo(it)
|
viewModel.addTodo(it)
|
||||||
viewModel.setEditTodoItem(null)
|
// viewModel.setEditTodoItem(null)
|
||||||
navController.navigateUp()
|
navController.navigateUp()
|
||||||
},
|
},
|
||||||
onDelete = {
|
onDelete = {
|
||||||
|
|
@ -82,7 +82,7 @@ fun TodoNavigation(
|
||||||
},
|
},
|
||||||
onNavigateUp = {
|
onNavigateUp = {
|
||||||
navController.navigateUp()
|
navController.navigateUp()
|
||||||
viewModel.setEditTodoItem(null)
|
// viewModel.setEditTodoItem(null)
|
||||||
},
|
},
|
||||||
sharedTransitionScope = this@SharedTransitionLayout,
|
sharedTransitionScope = this@SharedTransitionLayout,
|
||||||
animatedVisibilityScope = this@composable
|
animatedVisibilityScope = this@composable
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ fun TodoEditorPage(
|
||||||
animatedVisibilityScope: AnimatedVisibilityScope,
|
animatedVisibilityScope: AnimatedVisibilityScope,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
BackHandler {
|
BackHandler { // TODO: 实现用户更改后的阻止返回
|
||||||
onNavigateUp()
|
onNavigateUp()
|
||||||
}
|
}
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -141,18 +141,25 @@ fun TodoEditorPage(
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
TextField(
|
with(sharedTransitionScope) {
|
||||||
value = text,
|
TextField(
|
||||||
onValueChange = { text = it },
|
value = text,
|
||||||
label = { Text(stringResource(R.string.placeholder_add_todo)) },
|
onValueChange = { text = it },
|
||||||
isError = isError,
|
label = { Text(stringResource(R.string.placeholder_add_todo)) },
|
||||||
supportingText = {
|
isError = isError,
|
||||||
AnimatedVisibility(isError) {
|
supportingText = {
|
||||||
Text(stringResource(R.string.error_no_task_content))
|
AnimatedVisibility(isError) {
|
||||||
}
|
Text(stringResource(R.string.error_no_task_content))
|
||||||
},
|
}
|
||||||
modifier = Modifier.fillMaxWidth()
|
},
|
||||||
)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.sharedBounds(
|
||||||
|
sharedContentState = rememberSharedContentState("${Constants.KEY_TODO_CONTENT_TRANSITION}_${toDo?.id}"),
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(Modifier.size(5.dp))
|
Spacer(Modifier.size(5.dp))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,10 @@ fun MainPage(
|
||||||
) {
|
) {
|
||||||
TodoFAB(
|
TodoFAB(
|
||||||
expanded = isExpanded,
|
expanded = isExpanded,
|
||||||
onClick = toTodoEditPage,
|
onClick = {
|
||||||
|
viewModel.setEditTodoItem(null) // 每次添加待办前清除上一次已选待办
|
||||||
|
toTodoEditPage()
|
||||||
|
},
|
||||||
modifier = Modifier.sharedElement(
|
modifier = Modifier.sharedElement(
|
||||||
state = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
state = rememberSharedContentState(key = Constants.KEY_TODO_FAB_TRANSITION),
|
||||||
animatedVisibilityScope = animatedVisibilityScope
|
animatedVisibilityScope = animatedVisibilityScope
|
||||||
|
|
@ -146,6 +149,8 @@ fun MainPage(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectedTodoIds = selectedTodoIds.value,
|
selectedTodoIds = selectedTodoIds.value,
|
||||||
|
sharedTransitionScope = sharedTransitionScope,
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(3f)
|
.weight(3f)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -189,6 +194,8 @@ fun MainPage(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectedTodoIds = selectedTodoIds.value,
|
selectedTodoIds = selectedTodoIds.value,
|
||||||
|
sharedTransitionScope = sharedTransitionScope,
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(3f)
|
.weight(3f)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
package cn.super12138.todo.ui.pages.main
|
package cn.super12138.todo.ui.pages.main
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibilityScope
|
||||||
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
|
import androidx.compose.animation.SharedTransitionScope
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
|
@ -24,6 +27,7 @@ import cn.super12138.todo.ui.pages.main.components.TodoCard
|
||||||
import my.nanihadesuka.compose.LazyColumnScrollbar
|
import my.nanihadesuka.compose.LazyColumnScrollbar
|
||||||
import my.nanihadesuka.compose.ScrollbarSettings
|
import my.nanihadesuka.compose.ScrollbarSettings
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ManagerFragment(
|
fun ManagerFragment(
|
||||||
state: LazyListState,
|
state: LazyListState,
|
||||||
|
|
@ -32,6 +36,8 @@ fun ManagerFragment(
|
||||||
onItemLongClick: (TodoEntity) -> Unit = {},
|
onItemLongClick: (TodoEntity) -> Unit = {},
|
||||||
onItemChecked: (TodoEntity) -> Unit = {},
|
onItemChecked: (TodoEntity) -> Unit = {},
|
||||||
selectedTodoIds: List<Int>,
|
selectedTodoIds: List<Int>,
|
||||||
|
sharedTransitionScope: SharedTransitionScope,
|
||||||
|
animatedVisibilityScope: AnimatedVisibilityScope,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -66,6 +72,7 @@ fun ManagerFragment(
|
||||||
key = { it.id }
|
key = { it.id }
|
||||||
) { item ->
|
) { item ->
|
||||||
TodoCard(
|
TodoCard(
|
||||||
|
id = item.id,
|
||||||
content = item.content,
|
content = item.content,
|
||||||
subject = Subjects.fromId(item.subject).getDisplayName(context),
|
subject = Subjects.fromId(item.subject).getDisplayName(context),
|
||||||
completed = item.isCompleted,
|
completed = item.isCompleted,
|
||||||
|
|
@ -74,6 +81,8 @@ fun ManagerFragment(
|
||||||
onCardClick = { onItemClick(item) },
|
onCardClick = { onItemClick(item) },
|
||||||
onCardLongClick = { onItemLongClick(item) },
|
onCardLongClick = { onItemLongClick(item) },
|
||||||
onChecked = { onItemChecked(item) },
|
onChecked = { onItemChecked(item) },
|
||||||
|
sharedTransitionScope = sharedTransitionScope,
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(vertical = 5.dp)
|
.padding(vertical = 5.dp)
|
||||||
.animateItem() // TODO: 设置动画时间
|
.animateItem() // TODO: 设置动画时间
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package cn.super12138.todo.ui.pages.main.components
|
package cn.super12138.todo.ui.pages.main.components
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.AnimatedVisibilityScope
|
||||||
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
|
import androidx.compose.animation.SharedTransitionScope
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.basicMarquee
|
import androidx.compose.foundation.basicMarquee
|
||||||
|
|
@ -31,14 +34,15 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import cn.super12138.todo.R
|
import cn.super12138.todo.R
|
||||||
|
import cn.super12138.todo.constants.Constants
|
||||||
import cn.super12138.todo.logic.model.Priority
|
import cn.super12138.todo.logic.model.Priority
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TodoCard(
|
fun TodoCard(
|
||||||
|
id: Int,
|
||||||
content: String,
|
content: String,
|
||||||
subject: String,
|
subject: String,
|
||||||
completed: Boolean,
|
completed: Boolean,
|
||||||
|
|
@ -47,6 +51,8 @@ fun TodoCard(
|
||||||
onCardClick: () -> Unit = {},
|
onCardClick: () -> Unit = {},
|
||||||
onCardLongClick: () -> Unit = {},
|
onCardLongClick: () -> Unit = {},
|
||||||
onChecked: () -> Unit = {},
|
onChecked: () -> Unit = {},
|
||||||
|
sharedTransitionScope: SharedTransitionScope,
|
||||||
|
animatedVisibilityScope: AnimatedVisibilityScope,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -103,14 +109,21 @@ fun TodoCard(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(
|
with(sharedTransitionScope) {
|
||||||
text = content,
|
Text(
|
||||||
style = MaterialTheme.typography.titleLarge,
|
text = content,
|
||||||
maxLines = 1,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
overflow = TextOverflow.Ellipsis,
|
maxLines = 1,
|
||||||
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.basicMarquee() // TODO: 后续评估性能影响
|
textDecoration = if (completed) TextDecoration.LineThrough else TextDecoration.None,
|
||||||
)
|
modifier = Modifier
|
||||||
|
.sharedBounds(
|
||||||
|
sharedContentState = rememberSharedContentState("${Constants.KEY_TODO_CONTENT_TRANSITION}_$id"),
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope
|
||||||
|
)
|
||||||
|
.basicMarquee() // TODO: 后续评估性能影响
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
|
|
@ -151,6 +164,7 @@ fun TodoCard(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
@Preview(locale = "zh-rCN", showBackground = true)
|
@Preview(locale = "zh-rCN", showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TodoCardPreview() {
|
private fun TodoCardPreview() {
|
||||||
|
|
@ -162,6 +176,8 @@ private fun TodoCardPreview() {
|
||||||
selected = false,
|
selected = false,
|
||||||
onCardClick = {},
|
onCardClick = {},
|
||||||
onCardLongClick = {},
|
onCardLongClick = {},
|
||||||
onChecked = {}
|
onChecked = {},
|
||||||
|
sharedTransitionScope = ,
|
||||||
|
animatedVisibilityScope =
|
||||||
)
|
)
|
||||||
}
|
}*/
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue