Created
September 13, 2023 04:48
-
-
Save ahmedhosnypro/873b27a11d3238463749108489ae8be8 to your computer and use it in GitHub Desktop.
Jetpack Compose: Reorderable Vertical Grid with Swipe to Delete
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.... | |
implementation("org.burnoutcrew.composereorderable:reorderable:0.9.6") | |
..... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//.... | |
import androidx.compose.animation.core.animateDpAsState | |
import androidx.compose.animation.core.tween | |
import androidx.compose.foundation.ExperimentalFoundationApi | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.gestures.AnchoredDraggableState | |
import androidx.compose.foundation.gestures.DraggableAnchors | |
import androidx.compose.foundation.gestures.Orientation | |
import androidx.compose.foundation.gestures.anchoredDraggable | |
import androidx.compose.foundation.interaction.MutableInteractionSource | |
import androidx.compose.foundation.interaction.collectIsDraggedAsState | |
import androidx.compose.foundation.layout.Arrangement | |
import androidx.compose.foundation.layout.BoxWithConstraints | |
import androidx.compose.foundation.layout.fillMaxSize | |
import androidx.compose.foundation.layout.fillMaxWidth | |
import androidx.compose.foundation.layout.offset | |
import androidx.compose.foundation.layout.padding | |
import androidx.compose.foundation.lazy.LazyColumn | |
import androidx.compose.foundation.lazy.items | |
import androidx.compose.material3.Card | |
import androidx.compose.material3.CardDefaults | |
import androidx.compose.material3.Text | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.mutableStateOf | |
import androidx.compose.runtime.remember | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.draw.shadow | |
import androidx.compose.ui.graphics.Color | |
import androidx.compose.ui.layout.onSizeChanged | |
import androidx.compose.ui.platform.LocalDensity | |
import androidx.compose.ui.text.style.TextAlign | |
import androidx.compose.ui.tooling.preview.Preview | |
import androidx.compose.ui.unit.IntOffset | |
import androidx.compose.ui.unit.dp | |
import org.burnoutcrew.reorderable.ReorderableItem | |
import org.burnoutcrew.reorderable.detectReorderAfterLongPress | |
import org.burnoutcrew.reorderable.rememberReorderableLazyListState | |
import org.burnoutcrew.reorderable.reorderable | |
import kotlin.math.roundToInt | |
@OptIn(ExperimentalFoundationApi::class) | |
@Preview( | |
showBackground = true, | |
showSystemUi = true, | |
backgroundColor = 0x000000, | |
) | |
@Composable | |
fun ReorderedVerticalGridWithSwipeToDelete( | |
modifier: Modifier = Modifier, | |
) { | |
val items = remember { mutableStateOf(List(100) { "Item $it" }) } | |
val gridState = rememberReorderableLazyListState(onMove = { from, to -> | |
items.value = items.value.toMutableList().apply { | |
add(to.index, removeAt(from.index)) | |
} | |
}) | |
LazyColumn( | |
state = gridState.listState, | |
modifier = Modifier | |
.fillMaxSize() | |
.background(Color.Gray) | |
.reorderable(gridState) | |
.detectReorderAfterLongPress(gridState) | |
.padding(start = 8.dp, end = 8.dp), | |
horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally, | |
verticalArrangement = Arrangement.spacedBy(8.dp), | |
) { | |
items(items.value, { it }) { item -> | |
ReorderableItem(gridState, key = item) { isDragging -> | |
val elevation = animateDpAsState(if (isDragging) 16.dp else 0.dp, label = "") | |
// create de | |
val density = LocalDensity.current | |
val state = remember { | |
AnchoredDraggableState( | |
initialValue = DragAnchors.Start, | |
positionalThreshold = { distance: Float -> distance * .25f }, | |
velocityThreshold = { with(density) { 100.dp.toPx() } }, | |
animationSpec = tween(), | |
) | |
} | |
// Wrap the item in a BoxWithConstraints to get the maxWidth or maxHeight of the parent | |
BoxWithConstraints(modifier = Modifier.shadow(elevation.value)) { | |
// Get the maxWidth of the Grid | |
val parentWidth = this.maxWidth | |
// Convert the maxWidth to px to use it in the anchors as velocityThreshold | |
val contentSizePx = with(density) { parentWidth.toPx() } | |
// Create an interaction source to track the drag gesture | |
val interactionSource = remember { MutableInteractionSource() } | |
// Create a DragChangeDetector to detect when the drag ends | |
val dragChangeDetector = remember { DragChangeDetector() } | |
Card( | |
colors = CardDefaults.cardColors( | |
containerColor = Color.Black, | |
contentColor = Color(0xFFE0E0E0), | |
), | |
modifier = Modifier | |
.fillMaxWidth() | |
.onSizeChanged { layoutSize -> | |
val dragEndPoint = layoutSize.width + contentSizePx | |
state.updateAnchors(DraggableAnchors { | |
DragAnchors | |
.values() | |
.forEach { anchor -> | |
anchor at dragEndPoint * anchor.fraction | |
} | |
}) | |
} | |
.offset { | |
IntOffset( | |
state | |
.requireOffset() | |
.roundToInt(), 0 | |
) | |
} | |
.anchoredDraggable( | |
state, | |
Orientation.Horizontal, | |
interactionSource = interactionSource, | |
) | |
) { | |
Text( | |
text = item, | |
textAlign = TextAlign.Center, | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding(8.dp) | |
) | |
} | |
// Check if the item is dragged and if the closest anchor is the end anchor | |
val isDragged = interactionSource.collectIsDraggedAsState().value | |
val closestAnchor = state.anchors.closestAnchor(state.offset) | |
if (dragChangeDetector.dragEnd(isDragged) && | |
(closestAnchor == DragAnchors.End || closestAnchor == DragAnchors.LeftEnd) | |
) { | |
// remove item with id = item.id from items | |
items.value = items.value.toMutableList().apply { | |
// logic to remove the items from the list | |
removeIf { it === item } | |
} | |
} | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment