Skip to content

Commit 83e650e

Browse files
authored
Merge pull request #100 from aclassen/feature/refactor
Refactored to provide better animation support
2 parents 5b418c9 + 8229559 commit 83e650e

File tree

23 files changed

+700
-637
lines changed

23 files changed

+700
-637
lines changed

README.md

Lines changed: 17 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -15,86 +15,37 @@ dependencies {
1515

1616
## How to use
1717

18-
Create `reorderState` and add the `reorderable` Modifier to the LazyList/Grid:
19-
20-
```
21-
// For a LazyGrid just use `rememberReorderLazyListState`
22-
val state: ReorderableLazyListState = rememberReorderLazyListState(onMove = { from, to -> data.move(from.index, to.index) })
23-
24-
LazyColumn(
25-
state = state.listState,
26-
modifier = Modifier.reorderable(state)) {
27-
...
28-
}
29-
```
30-
31-
For a LazyGrid just use `rememberReorderLazyListState`
32-
To make an item reorderable/draggable add at least one drag modifier to the item:
33-
34-
```
35-
Modifier.detectReorder(state)
36-
or
37-
Modifier.detectReorderAfterLongPress(state)
38-
```
39-
40-
> Adding one of the detect modifiers to the LazyList instead of an item , will make all items reordable.
41-
42-
At least apply the dragged item offset:
43-
44-
```
45-
items(items, { it.key }) {item ->
46-
Column(
47-
modifier = Modifier.draggedItem(state.offsetByKey(item.key))
48-
) {
49-
...
50-
}
51-
}
52-
53-
or without keyed items:
54-
55-
itemsIndexed(items) { idx, item ->
56-
Column(
57-
modifier = Modifier.draggedItem(state.offsetByIndex(idx))
58-
) {
59-
...
60-
}
61-
}
62-
```
63-
64-
> You can use `draggedItem` for a default dragged effect or create your own.
65-
66-
Complete example:
6718
```
6819
@Composable
69-
fun ReorderableList(){
70-
val data = List(100) { "item $it" }.toMutableStateList()
71-
val state: ReorderableLazyListState = rememberReorderLazyListState(onMove = { from, to -> data.move(from.index, to.index) })
72-
20+
fun VerticalReorderList() {
21+
val data = remember { mutableStateOf(List(100) { "Item $it" }) }
22+
val state = rememberReorderableLazyListState(onMove = { from, to ->
23+
data.value = data.value.toMutableList().apply {
24+
add(to.index, removeAt(from.index))
25+
}
26+
})
7327
LazyColumn(
7428
state = state.listState,
7529
modifier = Modifier.reorderable(state)
7630
) {
77-
items(data, { it }) { item ->
78-
Box(
79-
modifier = Modifier
80-
.fillMaxWidth()
81-
.draggedItem(state.offsetByKey(item))
82-
.detectReorderAfterLongPress(state)
83-
) {
84-
Text(text = item)
31+
items(data.value, { it }) { item ->
32+
ReorderableItem(state, key = item) { isDragging ->
33+
Column(
34+
modifier = Modifier
35+
.background(MaterialTheme.colors.surface)
36+
.detectReorderAfterLongPress(state)
37+
) {
38+
Text(item)
39+
}
8540
}
8641
}
8742
}
8843
}
8944
```
9045

91-
9246
## Notes
9347

94-
When dragging, the existing item will be modified. Because of that it's important that the item must be part of the LazyList visible
95-
items all the time.
96-
97-
This can be problematic if no drop target can be found during scrolling.
48+
It's a known issue that the first visible item does not animate.
9849

9950
## License
10051

android/build.gradle.kts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ plugins {
55
}
66

77
dependencies {
8-
implementation("org.burnoutcrew.composereorderable:reorderable:0.8.1")
9-
implementation("androidx.compose.runtime:runtime:1.2.0-beta01")
10-
implementation("androidx.compose.material:material:1.2.0-beta01")
8+
implementation("org.burnoutcrew.composereorderable:reorderable:0.9.0")
9+
implementation("androidx.compose.runtime:runtime:1.2.0-beta02")
10+
implementation("androidx.compose.material:material:1.2.0-beta02")
1111
implementation("androidx.activity:activity-compose:1.4.0")
12-
implementation("com.google.android.material:material:1.6.0")
12+
implementation("com.google.android.material:material:1.6.1")
1313
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1")
1414
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1")
1515
implementation("androidx.navigation:navigation-compose:2.5.0-rc01")
16-
implementation("io.coil-kt:coil-compose:1.4.0")
16+
implementation("io.coil-kt:coil-compose:2.1.0")
1717
}
1818

1919
android {

android/src/main/kotlin/org/burnoutcrew/android/ui/reorderlist/ImageListViewModel.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 André Claßen
2+
* Copyright 2022 André Claßen
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,21 +15,22 @@
1515
*/
1616
package org.burnoutcrew.android.ui.reorderlist
1717

18-
import androidx.compose.runtime.toMutableStateList
18+
import androidx.compose.runtime.getValue
19+
import androidx.compose.runtime.mutableStateOf
20+
import androidx.compose.runtime.setValue
1921
import androidx.lifecycle.ViewModel
2022
import org.burnoutcrew.reorderable.ItemPosition
21-
import org.burnoutcrew.reorderable.move
22-
2323
import kotlin.random.Random
2424

2525

2626
class ImageListViewModel : ViewModel() {
27-
val images = List(20) { "https://picsum.photos/seed/compose$it/200/300" }.toMutableStateList()
2827
val headerImage = "https://picsum.photos/seed/compose${Random.nextInt(Int.MAX_VALUE)}/400/200"
2928
val footerImage = "https://picsum.photos/seed/compose${Random.nextInt(Int.MAX_VALUE)}/400/200"
30-
29+
var images by mutableStateOf(List(20) { "https://picsum.photos/seed/compose$it/200/300" })
3130
fun onMove(from: ItemPosition, to: ItemPosition) {
32-
images.move(images.indexOfFirst { it == from.key }, images.indexOfFirst { it == to.key })
31+
images = images.toMutableList().apply {
32+
add(images.indexOfFirst { it == to.key }, removeAt(images.indexOfFirst { it == from.key }))
33+
}
3334
}
3435

3536
fun canDragOver(pos: ItemPosition) = images.any { it == pos.key }

android/src/main/kotlin/org/burnoutcrew/android/ui/reorderlist/ItemData.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 André Claßen
2+
* Copyright 2022 André Claßen
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 André Claßen
2+
* Copyright 2022 André Claßen
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,10 +15,12 @@
1515
*/
1616
package org.burnoutcrew.android.ui.reorderlist
1717

18-
18+
import androidx.compose.animation.core.animateDpAsState
1919
import androidx.compose.foundation.background
20+
import androidx.compose.foundation.layout.Arrangement
2021
import androidx.compose.foundation.layout.Box
2122
import androidx.compose.foundation.layout.Column
23+
import androidx.compose.foundation.layout.PaddingValues
2224
import androidx.compose.foundation.layout.aspectRatio
2325
import androidx.compose.foundation.layout.height
2426
import androidx.compose.foundation.layout.padding
@@ -32,85 +34,96 @@ import androidx.compose.material.Text
3234
import androidx.compose.runtime.Composable
3335
import androidx.compose.ui.Alignment
3436
import androidx.compose.ui.Modifier
37+
import androidx.compose.ui.draw.shadow
3538
import androidx.compose.ui.unit.dp
3639
import androidx.lifecycle.viewmodel.compose.viewModel
37-
import org.burnoutcrew.reorderable.ItemPosition
40+
import org.burnoutcrew.reorderable.ReorderableItem
3841
import org.burnoutcrew.reorderable.detectReorderAfterLongPress
39-
import org.burnoutcrew.reorderable.draggedItem
4042
import org.burnoutcrew.reorderable.rememberReorderableLazyGridState
4143
import org.burnoutcrew.reorderable.reorderable
4244

43-
4445
@Composable
4546
fun ReorderGrid(vm: ReorderListViewModel = viewModel()) {
4647
Column {
4748
HorizontalGrid(
48-
items = vm.cats,
49-
modifier = Modifier.padding(vertical = 16.dp),
50-
onMove = { from, to -> vm.moveCat(from, to) },
51-
)
52-
VerticalGrid(
53-
items = vm.dogs,
54-
onMove = { from, to -> vm.moveDog(from, to) },
55-
canDragOver = { vm.isDogDragEnabled(it) },
49+
vm = vm,
50+
modifier = Modifier.padding(vertical = 16.dp)
5651
)
52+
VerticalGrid(vm = vm)
5753
}
5854
}
5955

60-
6156
@Composable
6257
private fun HorizontalGrid(
58+
vm: ReorderListViewModel,
6359
modifier: Modifier = Modifier,
64-
items: List<ItemData>,
65-
onMove: (fromPos: ItemPosition, toPos: ItemPosition) -> (Unit),
6660
) {
67-
val state = rememberReorderableLazyGridState(onMove = onMove)
61+
val state = rememberReorderableLazyGridState(onMove = vm::moveCat)
6862
LazyHorizontalGrid(
6963
rows = GridCells.Fixed(2),
7064
state = state.gridState,
65+
contentPadding = PaddingValues(horizontal = 8.dp),
66+
verticalArrangement = Arrangement.spacedBy(4.dp),
67+
horizontalArrangement = Arrangement.spacedBy(4.dp),
7168
modifier = modifier.reorderable(state).height(200.dp)
7269
) {
73-
items(items, { it.key }) { item ->
74-
Box(
75-
contentAlignment = Alignment.Center,
76-
modifier = Modifier
77-
.aspectRatio(1f)
78-
.padding(4.dp)
79-
.draggedItem(offset = state.offsetByKey(item.key))
80-
.background(MaterialTheme.colors.secondary)
81-
.detectReorderAfterLongPress(state)
82-
) {
83-
Text(item.title)
70+
items(vm.cats, { it.key }) { item ->
71+
ReorderableItem(state, item.key) { isDragging ->
72+
val elevation = animateDpAsState(if (isDragging) 16.dp else 0.dp)
73+
Box(
74+
contentAlignment = Alignment.Center,
75+
modifier = Modifier
76+
.shadow(elevation.value)
77+
.aspectRatio(1f)
78+
.background(MaterialTheme.colors.secondary)
79+
.detectReorderAfterLongPress(state)
80+
) {
81+
Text(item.title)
82+
}
8483
}
8584
}
8685
}
8786
}
8887

8988
@Composable
9089
private fun VerticalGrid(
90+
vm: ReorderListViewModel,
9191
modifier: Modifier = Modifier,
92-
items: List<ItemData>,
93-
onMove: (fromPos: ItemPosition, toPos: ItemPosition) -> (Unit),
94-
canDragOver: ((pos: ItemPosition) -> Boolean),
9592
) {
96-
val state = rememberReorderableLazyGridState(onMove = onMove, canDragOver = canDragOver)
93+
val state = rememberReorderableLazyGridState(onMove = vm::moveDog, canDragOver = vm::isDogDragEnabled)
9794
LazyVerticalGrid(
9895
columns = GridCells.Fixed(4),
9996
state = state.gridState,
97+
contentPadding = PaddingValues(horizontal = 8.dp),
98+
verticalArrangement = Arrangement.spacedBy(4.dp),
99+
horizontalArrangement = Arrangement.spacedBy(4.dp),
100100
modifier = modifier.reorderable(state)
101101
) {
102-
items(items, { it.key }) { item ->
103-
Box(
104-
contentAlignment = Alignment.Center,
105-
modifier = Modifier
106-
.size(100.dp)
107-
.padding(4.dp)
108-
.draggedItem(state.offsetByKey(item.key))
109-
.background(MaterialTheme.colors.primary)
110-
.detectReorderAfterLongPress(state)
111-
) {
112-
Text(item.title)
102+
items(vm.dogs, { it.key }) { item ->
103+
ReorderableItem(state, item.key) { isDragging ->
104+
val elevation = animateDpAsState(if (isDragging) 8.dp else 0.dp)
105+
if (item.isLocked) {
106+
Box(
107+
contentAlignment = Alignment.Center,
108+
modifier = Modifier
109+
.size(100.dp)
110+
.background(MaterialTheme.colors.surface)
111+
) {
112+
Text(item.title)
113+
}
114+
} else {
115+
Box(
116+
contentAlignment = Alignment.Center,
117+
modifier = Modifier
118+
.shadow(elevation.value)
119+
.aspectRatio(1f)
120+
.background(MaterialTheme.colors.primary)
121+
.detectReorderAfterLongPress(state)
122+
) {
123+
Text(item.title)
124+
}
125+
}
113126
}
114127
}
115128
}
116-
}
129+
}

0 commit comments

Comments
 (0)