feat: 分类管理页面 MD3E 适配
This commit is contained in:
parent
0fc2383e6c
commit
a487e4b890
5 changed files with 73 additions and 103 deletions
|
|
@ -16,8 +16,10 @@ import androidx.compose.material3.FilledTonalButton
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
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.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -58,7 +60,12 @@ fun SettingsAboutLicence(
|
|||
key = { _, library -> library.artifactId }
|
||||
) { index, library ->
|
||||
var openDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val topRounded by remember { derivedStateOf { index == 0 } }
|
||||
val bottomRounded by remember {
|
||||
derivedStateOf {
|
||||
index == (libraries?.libraries?.size ?: 1) - 1
|
||||
}
|
||||
}
|
||||
SettingsItem(
|
||||
headlineContent = {
|
||||
Row(
|
||||
|
|
@ -120,8 +127,8 @@ fun SettingsAboutLicence(
|
|||
}
|
||||
}
|
||||
},
|
||||
topRounded = index == 0,
|
||||
bottomRounded = index == (libraries?.libraries?.size ?: 1) - 1
|
||||
topRounded = topRounded,
|
||||
bottomRounded = bottomRounded
|
||||
)
|
||||
|
||||
BasicDialog(
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package cn.super12138.todo.ui.pages.settings
|
||||
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.VisibilityThreshold
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
|
|
@ -24,24 +24,29 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import cn.super12138.todo.R
|
||||
import cn.super12138.todo.logic.datastore.DataStoreManager
|
||||
import cn.super12138.todo.ui.components.TodoFloatingActionButton
|
||||
import cn.super12138.todo.ui.components.TopAppBarScaffold
|
||||
import cn.super12138.todo.ui.pages.settings.components.category.CategoryItem
|
||||
import cn.super12138.todo.ui.pages.settings.components.SettingsContainer
|
||||
import cn.super12138.todo.ui.pages.settings.components.SettingsItem
|
||||
import cn.super12138.todo.ui.pages.settings.components.category.CategoryPromptDialog
|
||||
import cn.super12138.todo.utils.VibrationUtils
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SettingsDataCategory(
|
||||
onNavigateUp: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
// TODO: 本页及其相关组件重组性能检查优化
|
||||
val view = LocalView.current
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val scope = rememberCoroutineScope()
|
||||
val listState = rememberLazyListState()
|
||||
|
|
@ -53,6 +58,7 @@ fun SettingsDataCategory(
|
|||
|
||||
val isExpanded by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
|
||||
|
||||
// TODO: 取消5字分类限制
|
||||
TopAppBarScaffold(
|
||||
title = stringResource(R.string.pref_category_category_management),
|
||||
onBack = onNavigateUp,
|
||||
|
|
@ -70,10 +76,7 @@ fun SettingsDataCategory(
|
|||
},
|
||||
modifier = modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
SettingsContainer(Modifier.fillMaxSize()) {
|
||||
if (categories.isEmpty()) {
|
||||
item {
|
||||
Text(
|
||||
|
|
@ -84,23 +87,50 @@ fun SettingsDataCategory(
|
|||
)
|
||||
}
|
||||
} else {
|
||||
items(items = categories, key = { it }) {
|
||||
CategoryItem(
|
||||
name = it,
|
||||
onClick = { category ->
|
||||
// Keep stable content key (category) for animations, but compute rounding based on content
|
||||
items(
|
||||
items = categories,
|
||||
key = { it }
|
||||
) { category ->
|
||||
// compute rounding by content rather than by index so insertion/moves
|
||||
// correctly update rounded corners while keeping content key for animations
|
||||
val topRounded = category == categories.first()
|
||||
val bottomRounded = category == categories.last()
|
||||
|
||||
SettingsItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = category,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.basicMarquee()
|
||||
)
|
||||
},
|
||||
trailingContent = {
|
||||
FilledTonalIconButton(
|
||||
shapes = IconButtonDefaults.shapes(),
|
||||
onClick = {
|
||||
VibrationUtils.performHapticFeedback(view)
|
||||
scope.launch { DataStoreManager.setCategories(categories - category) }
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_delete),
|
||||
contentDescription = stringResource(R.string.action_delete)
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
initialCategory = category
|
||||
showDialog = true
|
||||
},
|
||||
onDelete = { category ->
|
||||
scope.launch { DataStoreManager.setCategories(categories - category) }
|
||||
},
|
||||
topRounded = topRounded,
|
||||
bottomRounded = bottomRounded,
|
||||
modifier = Modifier.animateItem(
|
||||
fadeInSpec = tween(100),
|
||||
placementSpec = spring(
|
||||
stiffness = Spring.StiffnessMediumLow,
|
||||
visibilityThreshold = IntOffset.VisibilityThreshold
|
||||
),
|
||||
fadeOutSpec = tween(100)
|
||||
fadeInSpec = MaterialTheme.motionScheme.defaultEffectsSpec(),
|
||||
placementSpec = MaterialTheme.motionScheme.defaultSpatialSpec(),
|
||||
fadeOutSpec = MaterialTheme.motionScheme.defaultEffectsSpec()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,10 +230,12 @@ fun SettingsItem(
|
|||
.wrapContentHeight()
|
||||
.clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape))
|
||||
.clickable(
|
||||
enabled = enableClick, onClick = {
|
||||
enabled = enableClick,
|
||||
onClick = {
|
||||
VibrationUtils.performHapticFeedback(view)
|
||||
onClick()
|
||||
})
|
||||
}
|
||||
)
|
||||
.background(background)
|
||||
.padding(
|
||||
horizontal = TodoDefaults.settingsItemHorizontalPadding,
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
package cn.super12138.todo.ui.pages.settings.components.category
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import cn.super12138.todo.R
|
||||
import cn.super12138.todo.ui.TodoDefaults
|
||||
import cn.super12138.todo.utils.VibrationUtils
|
||||
|
||||
@Composable
|
||||
fun CategoryItem(
|
||||
modifier: Modifier = Modifier,
|
||||
name: String,
|
||||
onClick: (String) -> Unit = {},
|
||||
onDelete: (String) -> Unit = {}
|
||||
) {
|
||||
val view = LocalView.current
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.clip(MaterialTheme.shapes.large)
|
||||
.clickable(
|
||||
onClick = {
|
||||
VibrationUtils.performHapticFeedback(view)
|
||||
onClick(name)
|
||||
}
|
||||
)
|
||||
.padding(
|
||||
horizontal = TodoDefaults.settingsItemHorizontalPadding,
|
||||
vertical = TodoDefaults.settingsItemVerticalPadding / 2
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = name,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.bodyLarge.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
IconButton(
|
||||
onClick = {
|
||||
VibrationUtils.performHapticFeedback(view)
|
||||
onDelete(name)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_delete),
|
||||
contentDescription = stringResource(R.string.action_delete)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -123,6 +123,8 @@ fun SharedTransitionScope.TasksPage(
|
|||
items = filteredTodoList,
|
||||
key = { _, task -> task.id }
|
||||
) { index, task ->
|
||||
val topRounded by remember { derivedStateOf { index == 0 } }
|
||||
val bottomRounded by remember { derivedStateOf { index == filteredTodoList.size - 1 } }
|
||||
TodoCard(
|
||||
// id = item.id,
|
||||
content = task.content,
|
||||
|
|
@ -142,8 +144,8 @@ fun SharedTransitionScope.TasksPage(
|
|||
viewModel.updateTodo(task.copy(isCompleted = true))
|
||||
viewModel.playConfetti()
|
||||
},
|
||||
topRounded = index == 0,
|
||||
bottomRounded = index == filteredTodoList.size - 1,
|
||||
topRounded = topRounded,
|
||||
bottomRounded = bottomRounded,
|
||||
modifier = Modifier
|
||||
.sharedBounds(
|
||||
sharedContentState = rememberSharedContentState(key = "${Constants.KEY_TODO_ITEM_TRANSITION}_${task.id}"),
|
||||
|
|
|
|||
Loading…
Reference in a new issue