Making draggable items in LazyColumn. I have implemented the functionalities when drag after long press the IconButton. But what I want is drag immediately after start dragging. But simply changing detectDragGesturesAfterLongPress to detectDragGestures doesn't work.
@Composable
fun Screen2(modifier: Modifier = Modifier) {
val density = LocalDensity.current
val list = remember { mutableStateListOf(
"Kotlin", "Java", "C++", "Python", "C", "Assembly"
) }
var draggedItemIndex by remember { mutableIntStateOf(-1) }
val heightDp = remember { 80.dp } // Height of an item
val heightPx = remember { with(density) { heightDp.toPx() } }
LazyColumn(
modifier = modifier
) {
itemsIndexed(list, key = { _, k -> k }) { index, text ->
var dragOffsetY by remember { mutableFloatStateOf(0F) }
var isDragged by remember { mutableStateOf(false) }
Card(
modifier = Modifier
.padding(8.dp)
.graphicsLayer {
translationY = dragOffsetY
shadowElevation = if (isDragged) 8F else 0F
alpha = if (isDragged) .9F else 1F
}
.zIndex(if (isDragged) 2F else 0F)
.fillMaxWidth()
.then(if (isDragged) Modifier else Modifier.animateItem())
) {
Row(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text)
IconButton(
onClick = {},
modifier = Modifier
.pointerInput(index) {
detectDragGesturesAfterLongPress(
onDragStart = {
draggedItemIndex = index
isDragged = true
}
) { _, _ -> }
}
.pointerInput(Unit) {
detectDragGesturesAfterLongPress(
onDragEnd = {
dragOffsetY = 0F
draggedItemIndex = -1
isDragged = false
},
onDragCancel = {
dragOffsetY = 0F
draggedItemIndex = -1
isDragged = false
}
) { change, dragAmount ->
change.consume()
dragOffsetY += dragAmount.y
val indexOffset = (dragOffsetY / heightPx).toInt()
val newIndex = indexOffset + draggedItemIndex
if (newIndex in list.indices && newIndex != draggedItemIndex) {
Collections.swap(list, draggedItemIndex, newIndex)
draggedItemIndex = newIndex
dragOffsetY -= indexOffset * heightPx
}
}
}
) {
Icon(Icons.Default.DragHandle, "Drag Handle")
}
}
}
}
}
}
I am looking for solution such that an item should be able to drag immediately when drag from the IconButton.
Sample:
- First one is with detectDragGesturesAfterLongPress
- Second one is with detectDragGestures
Making draggable items in LazyColumn. I have implemented the functionalities when drag after long press the IconButton. But what I want is drag immediately after start dragging. But simply changing detectDragGesturesAfterLongPress to detectDragGestures doesn't work.
@Composable
fun Screen2(modifier: Modifier = Modifier) {
val density = LocalDensity.current
val list = remember { mutableStateListOf(
"Kotlin", "Java", "C++", "Python", "C", "Assembly"
) }
var draggedItemIndex by remember { mutableIntStateOf(-1) }
val heightDp = remember { 80.dp } // Height of an item
val heightPx = remember { with(density) { heightDp.toPx() } }
LazyColumn(
modifier = modifier
) {
itemsIndexed(list, key = { _, k -> k }) { index, text ->
var dragOffsetY by remember { mutableFloatStateOf(0F) }
var isDragged by remember { mutableStateOf(false) }
Card(
modifier = Modifier
.padding(8.dp)
.graphicsLayer {
translationY = dragOffsetY
shadowElevation = if (isDragged) 8F else 0F
alpha = if (isDragged) .9F else 1F
}
.zIndex(if (isDragged) 2F else 0F)
.fillMaxWidth()
.then(if (isDragged) Modifier else Modifier.animateItem())
) {
Row(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text)
IconButton(
onClick = {},
modifier = Modifier
.pointerInput(index) {
detectDragGesturesAfterLongPress(
onDragStart = {
draggedItemIndex = index
isDragged = true
}
) { _, _ -> }
}
.pointerInput(Unit) {
detectDragGesturesAfterLongPress(
onDragEnd = {
dragOffsetY = 0F
draggedItemIndex = -1
isDragged = false
},
onDragCancel = {
dragOffsetY = 0F
draggedItemIndex = -1
isDragged = false
}
) { change, dragAmount ->
change.consume()
dragOffsetY += dragAmount.y
val indexOffset = (dragOffsetY / heightPx).toInt()
val newIndex = indexOffset + draggedItemIndex
if (newIndex in list.indices && newIndex != draggedItemIndex) {
Collections.swap(list, draggedItemIndex, newIndex)
draggedItemIndex = newIndex
dragOffsetY -= indexOffset * heightPx
}
}
}
) {
Icon(Icons.Default.DragHandle, "Drag Handle")
}
}
}
}
}
}
I am looking for solution such that an item should be able to drag immediately when drag from the IconButton.
Sample:
- First one is with detectDragGesturesAfterLongPress
- Second one is with detectDragGestures
2 Answers
Reset to default 0With your code I was able to try something like this:
@Composable
fun Screen2(modifier: Modifier = Modifier) {
val density = LocalDensity.current
val list = remember { mutableStateListOf(
"Kotlin", "Java", "C++", "Python", "C", "Assembly"
) }
var draggedItemIndex by remember { mutableIntStateOf(-1) }
val heightDp = remember { 80.dp }
val heightPx = remember { with(density) { heightDp.toPx() } }
LazyColumn(
modifier = modifier
) {
itemsIndexed(list, key = { _, k -> k }) { index, text ->
var dragOffsetY by remember { mutableFloatStateOf(0F) }
var isDragged by remember { mutableStateOf(false) }
Card(
modifier = Modifier
.padding(8.dp)
.graphicsLayer {
translationY = dragOffsetY
shadowElevation = if (isDragged) 8F else 0F
alpha = if (isDragged) .9F else 1F
}
.zIndex(if (isDragged) 2F else 0F)
.fillMaxWidth()
) {
Row(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text)
IconButton(
onClick = {},
modifier = Modifier
.pointerInput(Unit) {
detectDragGestures(
onDragStart = { offset ->
draggedItemIndex = index
isDragged = true
},
onDrag = { change, dragAmount ->
change.consume()
dragOffsetY += dragAmount.y
val indexOffset = (dragOffsetY / heightPx).toInt()
val newIndex = indexOffset + draggedItemIndex
if (newIndex in list.indices && newIndex != draggedItemIndex) {
Collections.swap(list, draggedItemIndex, newIndex)
draggedItemIndex = newIndex
dragOffsetY -= indexOffset * heightPx
}
},
onDragEnd = {
dragOffsetY = 0F
draggedItemIndex = -1
isDragged = false
},
onDragCancel = {
dragOffsetY = 0F
draggedItemIndex = -1
isDragged = false
}
)
}
) {
Icon(Icons.Rounded.Menu, "Drag Handle")
}
}
}
}
}
}
It's by no means perfect, but it's a step forward in terms of moving items. I've found an interesting library where they go deeper into the reflection. I hope that could help you. https://github/Calvin-LL/Reorderable
【Here is machine translation】
This is one of the issues where I've gotten the most bang for my buck lately! So while it may not solve your problem, I'd like to give you my implementation code for your reference. Add the dependency first
implementation("sh.calvin.reorderable:reorderable:2.4.3")
After reading your questions and answering the discussion, I believe that the code solution fulfills two of your requirements:
Reorder by dragging and dropping icon
Exchange the specified columns correctly
@Composable
fun VerticalReorderList(routineList:List<Routine>) {
val view = LocalView.current
/*
* Custom data classes are also compatible if required.-use my data class example here
*/
var list = remember { mutableStateListOf<Routine>().apply {
addAll(routineList)
} }
val lazyListState = rememberLazyListState()
val reorderableLazyListState = rememberReorderableLazyListState(lazyListState) { from, to ->
list.apply {
add(to.index, removeAt(from.index))
}
ViewCompat.performHapticFeedback(
view,
HapticFeedbackConstantsCompat.SEGMENT_FREQUENT_TICK
)
}
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = lazyListState,
) {
items(
items = list,
key = { it.id }
) {routine ->
ReorderableItem(
state = reorderableLazyListState,
key = routine.id
) { isDragging ->
val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp)
Surface(shadowElevation = elevation) {
/*
* can be replaced with any UI design
*/
DetailRoutineRow(
modifier = Modifier
.clickable{ } //The function here is intended to illustrate the compatibility of multiple gesture detections
.swipeToDismiss { },
) {
IconButton(
modifier = Modifier.draggableHandle(
onDragStarted = {
ViewCompat.performHapticFeedback(
view,
HapticFeedbackConstantsCompat.GESTURE_START
)
},
onDragStopped = {
ViewCompat.performHapticFeedback(
view,
HapticFeedbackConstantsCompat.GESTURE_END
)
},
),
onClick = {},
) {
Icon(Icons.Rounded.DragHandle, contentDescription = "Reorder")
}
}
}
}
}
}
}
by the way,anyone Know how to add images? my gif forever "Image upload failed. Please try again."
detectDragGestures
– Edric Commented Mar 11 at 1:22