refactor(ui): Vereinheitlichung des Drag-and-Drop-Verhaltens

Das Drag-and-Drop-Verhalten für Einkaufslisten und deren Einträge wurde überarbeitet und vereinheitlicht, um eine konsistentere und intuitivere Benutzererfahrung zu schaffen.

- Das separate Verschiebe-Icon wurde sowohl in der Listen- als auch in der Detailansicht entfernt. Stattdessen kann nun das gesamte Element (Karte oder Zeile) durch langes Drücken verschoben werden.

- In der Detailansicht werden die Artikel nun als 'Cards' dargestellt, um beim Verschieben ein klares visuelles Feedback (Schatten/Anhebung) zu geben, analog zur Listenansicht.

- Das Bearbeiten-Icon in der Detailansicht wurde durch ein "Mehr"-Menü ersetzt. Dieses Menü bietet nun die Optionen "Umbenennen" und "Menge ändern" und sorgt für eine klare und explizite Interaktion.
This commit is contained in:
2025-10-12 19:57:30 +02:00
parent 85bd89cce1
commit 04876d1860
2 changed files with 51 additions and 27 deletions

View File

@@ -15,9 +15,14 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.rounded.DragHandle
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -147,38 +152,60 @@ fun ShoppingListDetailScreen(
) {
items(filteredItems, { it.id }) { item ->
ReorderableItem(reorderableLazyListState, key = item.id) { isDragging ->
val elevation = animateDpAsState(if (isDragging) 0.dp else 0.dp, label = "elevation")
val elevation = animateDpAsState(if (isDragging) 4.dp else 0.dp, label = "elevation")
val scale = animateFloatAsState(if (isDragging) 1.05f else 1.0f, label = "scale")
val isSelected = item == selectedItem
val backgroundColor = animateColorAsState(
if (isDragging) MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.7f)
else if (isSelected) MaterialTheme.colorScheme.surfaceVariant
else Color.Transparent, label = "backgroundColor"
)
Row(
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
.draggableHandle()
.graphicsLayer {
scaleX = scale.value
scaleY = scale.value
}
.shadow(elevation.value)
.background(backgroundColor.value)
.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { coroutineScope.launch {
viewModel.saveShoppingListItem(item.copy(isChecked = !item.isChecked))
.shadow(elevation.value),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { // The check/uncheck logic
coroutineScope.launch {
viewModel.saveShoppingListItem(item.copy(isChecked = !item.isChecked))
}
}
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (item.quantity != null) "${item.name} (${item.quantity})" else item.name,
style = MaterialTheme.typography.bodyLarge,
textDecoration = if (item.isChecked) TextDecoration.LineThrough else null,
modifier = Modifier.weight(1f)
)
var showMenu by remember { mutableStateOf(false) }
IconButton(onClick = {
viewModel.onSelectItem(item)
showMenu = true
}) {
Icon(Icons.Default.MoreVert, contentDescription = "More options")
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
DropdownMenuItem(
text = { Text(stringResource(R.string.rename_item_title)) },
onClick = {
viewModel.onShowRenameDialog(true)
showMenu = false
}
)
DropdownMenuItem(
text = { Text(stringResource(R.string.change_quantity)) },
onClick = {
viewModel.onShowQuantityDialog(true)
showMenu = false
}
)
}
}
.padding(vertical = 8.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = if (item.quantity != null) "${item.name} (${item.quantity})" else item.name,
style = MaterialTheme.typography.bodyLarge,
textDecoration = if (item.isChecked) TextDecoration.LineThrough else null,
modifier = Modifier.weight(1f)
)
IconButton(modifier = Modifier.draggableHandle(onDragStarted = { viewModel.onSelectItem(item) }), onClick = { }) {
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
}
}
}

View File

@@ -175,7 +175,7 @@ fun ShoppingListCard(
else Color.Transparent, label = "backgroundColor"
)
Card(
modifier = modifier
modifier = with(scope) { modifier.draggableHandle() }
.fillMaxWidth()
.padding(vertical = 4.dp, horizontal = 16.dp)
.graphicsLayer {
@@ -202,9 +202,6 @@ fun ShoppingListCard(
IconButton(onClick = { onDeleteList(listWithItems) }) {
Icon(Icons.Default.Delete, contentDescription = stringResource(R.string.delete_list))
}
IconButton(modifier = with(scope) { Modifier.draggableHandle() }, onClick = { }) {
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
}
}
}
}