feat: 支持多选和删除待办(后续优化逻辑)
This commit is contained in:
parent
1656063449
commit
366f19ca06
11 changed files with 256 additions and 41 deletions
|
|
@ -19,6 +19,14 @@ object Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteTodo(toDo: TodoEntity) {
|
suspend fun deleteTodo(toDo: TodoEntity) {
|
||||||
toDoDao.delete(toDo.id)
|
toDoDao.delete(toDo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun deleteTodoFromIds(toDoItems: List<Int>) {
|
||||||
|
toDoDao.deleteFromIds(toDoItems.toSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
/*suspend fun deleteAllTodo() {
|
||||||
|
toDoDao.deleteAllTodo()
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.super12138.todo.logic.database
|
package cn.super12138.todo.logic.database
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
|
@ -18,6 +19,12 @@ interface TodoDao {
|
||||||
@Update
|
@Update
|
||||||
suspend fun update(toDo: TodoEntity)
|
suspend fun update(toDo: TodoEntity)
|
||||||
|
|
||||||
@Query("DELETE FROM todo WHERE id = :toDoId")
|
@Delete
|
||||||
suspend fun delete(toDoId: Int)
|
suspend fun delete(toDo: TodoEntity)
|
||||||
|
|
||||||
|
@Query("DELETE FROM todo WHERE id in (:toDoIds)")
|
||||||
|
suspend fun deleteFromIds(toDoIds: Set<Int>)
|
||||||
|
|
||||||
|
/*@Query("DELETE FROM todo")
|
||||||
|
suspend fun deleteAllTodo()*/
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,6 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import cn.super12138.todo.ui.TodoDefaults
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 带动画且比 Material 3 内置组件动画好看的的可扩展 FAB
|
* 带动画且比 Material 3 内置组件动画好看的的可扩展 FAB
|
||||||
|
|
@ -36,7 +35,7 @@ fun AnimatedExtendedFloatingActionButton(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(horizontal = TodoDefaults.screenPadding),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
icon()
|
icon()
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,31 @@
|
||||||
package cn.super12138.todo.ui.pages.main
|
package cn.super12138.todo.ui.pages.main
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.animateColorAsState
|
||||||
|
import androidx.compose.animation.expandIn
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.shrinkOut
|
||||||
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.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Close
|
||||||
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
|
import androidx.compose.material.icons.outlined.SelectAll
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -44,36 +57,100 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectedEditTodoItem = viewModel.selectedEditTodoItem
|
val selectedEditTodoItem = viewModel.selectedEditTodo
|
||||||
|
val selectedTodoIds = viewModel.selectedTodoIds.collectAsState()
|
||||||
var openBottomSheet by rememberSaveable { mutableStateOf(false) }
|
var openBottomSheet by rememberSaveable { mutableStateOf(false) }
|
||||||
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
|
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
|
||||||
|
|
||||||
|
val isSelectedIdsEmpty by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
selectedTodoIds.value.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val animatedTopAppBarColors by animateColorAsState(
|
||||||
|
targetValue = if (isSelectedIdsEmpty) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.surfaceContainerHighest
|
||||||
|
)
|
||||||
|
|
||||||
|
BackHandler(enabled = !isSelectedIdsEmpty) {
|
||||||
|
// 当按下返回键(或进行返回操作)时清空选择
|
||||||
|
viewModel.clearAllTodoSelection()
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
navigationIcon = {
|
||||||
|
AnimatedVisibility(!isSelectedIdsEmpty) {
|
||||||
|
IconButton(onClick = { viewModel.clearAllTodoSelection() }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Close,
|
||||||
|
contentDescription = stringResource(R.string.tip_clear_selected_items)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
title = {
|
title = {
|
||||||
Text(stringResource(R.string.app_name))
|
Text(
|
||||||
|
text = if (isSelectedIdsEmpty) {
|
||||||
|
stringResource(R.string.app_name)
|
||||||
|
} else {
|
||||||
|
stringResource(
|
||||||
|
R.string.title_selected_count,
|
||||||
|
selectedTodoIds.value.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(
|
AnimatedContent(isSelectedIdsEmpty) { isEmpty ->
|
||||||
onClick = {}
|
if (isEmpty) {
|
||||||
) {
|
IconButton(
|
||||||
Icon(
|
onClick = {}
|
||||||
imageVector = Icons.Outlined.Settings,
|
) {
|
||||||
contentDescription = stringResource(R.string.page_settings)
|
Icon(
|
||||||
)
|
imageVector = Icons.Outlined.Settings,
|
||||||
|
contentDescription = stringResource(R.string.page_settings)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Row {
|
||||||
|
IconButton(onClick = { viewModel.toggleAllSelected() }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.SelectAll,
|
||||||
|
contentDescription = stringResource(R.string.tip_select_all)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(onClick = { viewModel.deleteSelectedTodo() }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Delete,
|
||||||
|
contentDescription = stringResource(R.string.action_delete)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
},
|
||||||
|
colors = TopAppBarDefaults.largeTopAppBarColors().copy(
|
||||||
|
containerColor = animatedTopAppBarColors
|
||||||
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
TodoFAB(
|
AnimatedVisibility(
|
||||||
expanded = isExpanded,
|
visible = isSelectedIdsEmpty,
|
||||||
onClick = { openBottomSheet = true }
|
enter = fadeIn() + expandIn(),
|
||||||
)
|
exit = shrinkOut() + fadeOut()
|
||||||
|
) {
|
||||||
|
TodoFAB(
|
||||||
|
expanded = isExpanded,
|
||||||
|
onClick = { openBottomSheet = true }
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
|
|
@ -91,8 +168,15 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||||
state = listState,
|
state = listState,
|
||||||
list = toDoList.value.filter { item -> !item.isCompleted },
|
list = toDoList.value.filter { item -> !item.isCompleted },
|
||||||
onItemClick = { item ->
|
onItemClick = { item ->
|
||||||
openBottomSheet = true
|
if (isSelectedIdsEmpty) {
|
||||||
viewModel.setEditTodoItem(item)
|
openBottomSheet = true
|
||||||
|
viewModel.setEditTodoItem(item)
|
||||||
|
} else {
|
||||||
|
viewModel.toggleTodoSelection(item)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onItemLongClick = { item ->
|
||||||
|
viewModel.toggleTodoSelection(item)
|
||||||
},
|
},
|
||||||
onItemChecked = { item ->
|
onItemChecked = { item ->
|
||||||
item.apply {
|
item.apply {
|
||||||
|
|
@ -107,6 +191,7 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||||
viewModel.playConfetti()
|
viewModel.playConfetti()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
selectedTodoIds = selectedTodoIds.value,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(3f)
|
.weight(3f)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -126,8 +211,15 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||||
state = listState,
|
state = listState,
|
||||||
list = toDoList.value.filter { item -> !item.isCompleted },
|
list = toDoList.value.filter { item -> !item.isCompleted },
|
||||||
onItemClick = { item ->
|
onItemClick = { item ->
|
||||||
openBottomSheet = true
|
if (isSelectedIdsEmpty) {
|
||||||
viewModel.setEditTodoItem(item)
|
openBottomSheet = true
|
||||||
|
viewModel.setEditTodoItem(item)
|
||||||
|
} else {
|
||||||
|
viewModel.toggleTodoSelection(item)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onItemLongClick = { item ->
|
||||||
|
viewModel.toggleTodoSelection(item)
|
||||||
},
|
},
|
||||||
onItemChecked = { item ->
|
onItemChecked = { item ->
|
||||||
item.apply {
|
item.apply {
|
||||||
|
|
@ -142,6 +234,7 @@ fun MainPage(viewModel: MainViewModel, modifier: Modifier = Modifier) {
|
||||||
viewModel.playConfetti()
|
viewModel.playConfetti()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
selectedTodoIds = selectedTodoIds.value,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(3f)
|
.weight(3f)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ fun ManagerFragment(
|
||||||
state: LazyListState,
|
state: LazyListState,
|
||||||
list: List<TodoEntity>,
|
list: List<TodoEntity>,
|
||||||
onItemClick: (TodoEntity) -> Unit = {},
|
onItemClick: (TodoEntity) -> Unit = {},
|
||||||
|
onItemLongClick: (TodoEntity) -> Unit = {},
|
||||||
onItemChecked: (TodoEntity) -> Unit = {},
|
onItemChecked: (TodoEntity) -> Unit = {},
|
||||||
|
selectedTodoIds: List<Int>,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -67,10 +69,12 @@ fun ManagerFragment(
|
||||||
content = item.content,
|
content = item.content,
|
||||||
subject = Subjects.fromId(item.subject).getDisplayName(context),
|
subject = Subjects.fromId(item.subject).getDisplayName(context),
|
||||||
onCardClick = { onItemClick(item) },
|
onCardClick = { onItemClick(item) },
|
||||||
|
onCardLongClick = { onItemLongClick(item) },
|
||||||
onChecked = { onItemChecked(item) },
|
onChecked = { onItemChecked(item) },
|
||||||
|
selected = selectedTodoIds.contains(item.id),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(vertical = 5.dp)
|
.padding(vertical = 5.dp)
|
||||||
.animateItem()
|
.animateItem() // TODO: 设置动画时间
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
package cn.super12138.todo.ui.pages.main.components
|
package cn.super12138.todo.ui.pages.main.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.basicMarquee
|
import androidx.compose.foundation.basicMarquee
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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.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
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Check
|
import androidx.compose.material.icons.outlined.Check
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
|
|
@ -18,31 +24,53 @@ import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
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.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
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
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TodoCard(
|
fun TodoCard(
|
||||||
content: String,
|
content: String,
|
||||||
subject: String,
|
subject: String,
|
||||||
onCardClick: () -> Unit = {},
|
onCardClick: () -> Unit = {},
|
||||||
|
onCardLongClick: () -> Unit = {},
|
||||||
onChecked: () -> Unit = {},
|
onChecked: () -> Unit = {},
|
||||||
|
selected: Boolean,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(80.dp),
|
.height(80.dp)
|
||||||
onClick = onCardClick
|
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = onCardClick,
|
||||||
|
onLongClick = onCardLongClick
|
||||||
|
)
|
||||||
.padding(horizontal = 15.dp)
|
.padding(horizontal = 15.dp)
|
||||||
) {
|
) {
|
||||||
|
AnimatedVisibility(selected) {
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.padding(end = 15.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(MaterialTheme.colorScheme.onSecondaryContainer)
|
||||||
|
.padding(5.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Check,
|
||||||
|
tint = MaterialTheme.colorScheme.onSecondary,
|
||||||
|
contentDescription = stringResource(R.string.tip_select_this)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -63,12 +91,14 @@ fun TodoCard(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton(onClick = onChecked) {
|
AnimatedVisibility(!selected) {
|
||||||
Icon(
|
IconButton(onClick = onChecked) {
|
||||||
imageVector = Icons.Outlined.Check,
|
Icon(
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
imageVector = Icons.Outlined.Check,
|
||||||
contentDescription = stringResource(R.string.tip_mark_completed)
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
)
|
contentDescription = stringResource(R.string.tip_mark_completed)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Box(
|
/*Box(
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,22 @@ import androidx.lifecycle.viewModelScope
|
||||||
import cn.super12138.todo.logic.Repository
|
import cn.super12138.todo.logic.Repository
|
||||||
import cn.super12138.todo.logic.database.TodoEntity
|
import cn.super12138.todo.logic.database.TodoEntity
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MainViewModel : ViewModel() {
|
class MainViewModel : ViewModel() {
|
||||||
val toDos: Flow<List<TodoEntity>> = Repository.getAllTodos()
|
val toDos: Flow<List<TodoEntity>> = Repository.getAllTodos()
|
||||||
var selectedEditTodoItem by mutableStateOf<TodoEntity?>(null)
|
var selectedEditTodo by mutableStateOf<TodoEntity?>(null)
|
||||||
private set
|
private set
|
||||||
val showConfetti = mutableStateOf(false)
|
val showConfetti = mutableStateOf(false)
|
||||||
|
|
||||||
|
// 多选逻辑参考:https://github.com/X1nto/Mauth
|
||||||
|
private val _selectedTodoIds = MutableStateFlow(listOf<Int>())
|
||||||
|
val selectedTodoIds = _selectedTodoIds.asStateFlow()
|
||||||
|
|
||||||
fun addTodo(toDo: TodoEntity) {
|
fun addTodo(toDo: TodoEntity) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
Repository.insertTodo(toDo)
|
Repository.insertTodo(toDo)
|
||||||
|
|
@ -28,17 +36,75 @@ class MainViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playConfetti() {
|
|
||||||
showConfetti.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setEditTodoItem(toDo: TodoEntity?) {
|
|
||||||
selectedEditTodoItem = toDo
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteTodo(toDo: TodoEntity) {
|
fun deleteTodo(toDo: TodoEntity) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
Repository.deleteTodo(toDo)
|
Repository.deleteTodo(toDo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*fun deleteAllTodo() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
Repository.deleteAllTodo()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
fun setEditTodoItem(toDo: TodoEntity?) {
|
||||||
|
selectedEditTodo = toDo
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换待办的选择状态
|
||||||
|
*/
|
||||||
|
fun toggleTodoSelection(toDo: TodoEntity) {
|
||||||
|
_selectedTodoIds.update { idList ->
|
||||||
|
if (idList.contains(toDo.id)) {
|
||||||
|
// 若已经选择取消选择
|
||||||
|
idList - toDo.id
|
||||||
|
} else {
|
||||||
|
// 若未选择添加到列表中,立即选中
|
||||||
|
idList + toDo.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换是否全选
|
||||||
|
*/
|
||||||
|
fun toggleAllSelected() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
toDos.firstOrNull()?.let { todos ->
|
||||||
|
val allIds = todos.map { it.id }
|
||||||
|
_selectedTodoIds.update { currentSelectedIds ->
|
||||||
|
if (currentSelectedIds.containsAll(allIds)) {
|
||||||
|
// 如果当前是全选状态,取消所有选择(切换为全不选)
|
||||||
|
emptyList()
|
||||||
|
} else {
|
||||||
|
// 如果当前不是全选状态,选中所有项
|
||||||
|
allIds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除全部已选择的待办
|
||||||
|
*/
|
||||||
|
fun clearAllTodoSelection() {
|
||||||
|
_selectedTodoIds.update { emptyList() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除选择的待办
|
||||||
|
*/
|
||||||
|
fun deleteSelectedTodo() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
Repository.deleteTodoFromIds(selectedTodoIds.value)
|
||||||
|
clearAllTodoSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun playConfetti() {
|
||||||
|
showConfetti.value = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="Theme.ToDo" parent="android:Theme.Material.NoActionBar">
|
<style name="Theme.ToDo" parent="android:Theme.Material.NoActionBar">
|
||||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,7 @@
|
||||||
<string name="tip_no_crash_logs">没有日志传入</string>
|
<string name="tip_no_crash_logs">没有日志传入</string>
|
||||||
<string name="action_exit_app">退出应用</string>
|
<string name="action_exit_app">退出应用</string>
|
||||||
<string name="action_delete">删除</string>
|
<string name="action_delete">删除</string>
|
||||||
|
<string name="tip_clear_selected_items">清除已选择的项目</string>
|
||||||
|
<string name="tip_select_all">全选</string>
|
||||||
|
<string name="title_selected_count">已选择 %s 项</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -25,4 +25,7 @@
|
||||||
<string name="tip_no_crash_logs">No crash logs</string>
|
<string name="tip_no_crash_logs">No crash logs</string>
|
||||||
<string name="action_exit_app">Exit app</string>
|
<string name="action_exit_app">Exit app</string>
|
||||||
<string name="action_delete">Delete</string>
|
<string name="action_delete">Delete</string>
|
||||||
|
<string name="tip_clear_selected_items">Clear selected items</string>
|
||||||
|
<string name="tip_select_all">Select all</string>
|
||||||
|
<string name="title_selected_count">%s selected</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="Theme.ToDo" parent="android:Theme.Material.Light.NoActionBar">
|
<style name="Theme.ToDo" parent="android:Theme.Material.Light.NoActionBar">
|
||||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue