From a487e4b890528a5cf74f6a1802107961198b0eba Mon Sep 17 00:00:00 2001 From: Super12138 <70494801+Super12138@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:44:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=86=E7=B1=BB=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=20MD3E=20=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/pages/settings/SettingsAboutLicence.kt | 13 ++- .../ui/pages/settings/SettingsDataCategory.kt | 80 +++++++++++++------ .../pages/settings/components/SettingsItem.kt | 6 +- .../components/category/CategoryItem.kt | 71 ---------------- .../todo/ui/pages/tasks/TasksPage.kt | 6 +- 5 files changed, 73 insertions(+), 103 deletions(-) delete mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/category/CategoryItem.kt diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt index b79d198..4797528 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsAboutLicence.kt @@ -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( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt index 162834c..fc6e788 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/SettingsDataCategory.kt @@ -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() ) ) } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt index bf4f4e6..9be0799 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/SettingsItem.kt @@ -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, diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/category/CategoryItem.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/category/CategoryItem.kt deleted file mode 100644 index c858d9f..0000000 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/settings/components/category/CategoryItem.kt +++ /dev/null @@ -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) - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/tasks/TasksPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/tasks/TasksPage.kt index f4922f8..83a5b10 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/tasks/TasksPage.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/tasks/TasksPage.kt @@ -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}"),