Initial commit

This commit is contained in:
vfsfitvnm
2022-06-02 18:59:18 +02:00
commit 1e673ad582
160 changed files with 10800 additions and 0 deletions

1
compose-reordering/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,46 @@
plugins {
id("com.android.library")
kotlin("android")
}
android {
namespace = "it.vfsfitvnm.reordering"
compileSdk = 32
defaultConfig {
minSdk = 21
targetSdk = 32
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
}
}
sourceSets.all {
kotlin.srcDir("src/$name/kotlin")
}
buildFeatures {
compose = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.compose.get()
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation(libs.compose.foundation)
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"/>

View File

@@ -0,0 +1,65 @@
package it.vfsfitvnm.reordering
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.animateIntAsState
import androidx.compose.runtime.*
class ReorderingState(
draggingIndexState: MutableState<Int>,
reachedIndexState: MutableState<Int>,
draggingItemSizeState: MutableState<Int>,
internal val offset: Animatable<Int, AnimationVector1D>,
internal val lastIndex: Int,
internal val areEquals: (Int, Int) -> Boolean
) {
internal var draggingIndex by draggingIndexState
internal var reachedIndex by reachedIndexState
internal var draggingItemSize by draggingItemSizeState
@Composable
internal fun translationFor(index: Int): State<Int> = when (draggingIndex) {
-1 -> derivedStateOf { 0 }
index -> offset.asState()
else -> animateIntAsState(
when (index) {
in (draggingIndex + 1)..reachedIndex -> -draggingItemSize
in reachedIndex until draggingIndex -> draggingItemSize
else -> 0
}
)
}
}
@Composable
fun rememberReorderingState(items: List<Any>): ReorderingState {
val draggingIndexState = remember(items) {
mutableStateOf(-1)
}
val reachedIndexState = remember(items) {
mutableStateOf(-1)
}
val draggingItemHeightState = remember {
mutableStateOf(0)
}
val offset = remember(items) {
Animatable(0, Int.VectorConverter)
}
return remember(items) {
ReorderingState(
draggingIndexState = draggingIndexState,
reachedIndexState = reachedIndexState,
draggingItemSizeState = draggingItemHeightState,
offset = offset,
lastIndex = items.lastIndex,
areEquals = { i, j ->
items[i] == items[j]
}
)
}
}

View File

@@ -0,0 +1,208 @@
package it.vfsfitvnm.reordering
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.zIndex
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.roundToInt
import kotlin.reflect.KSuspendFunction5
private fun Modifier.dragToReorder(
reorderingState: ReorderingState,
index: Int,
orientation: Orientation,
function: KSuspendFunction5<PointerInputScope, (Offset) -> Unit, () -> Unit, () -> Unit, (change: PointerInputChange, dragAmount: Offset) -> Unit, Unit>,
onDragStart: (() -> Unit)? = null,
onMove: (() -> Unit)? = null,
onDragEnd: ((Int) -> Unit)? = null
): Modifier = composed {
val coroutineScope = rememberCoroutineScope()
val translation by reorderingState.translationFor(index)
pointerInput(reorderingState) {
// require(index in 0..reorderingState.lastIndex)
var previousItemSize = 0
var nextItemSize = 0
function(
this,
{
onDragStart?.invoke()
reorderingState.draggingIndex = index
reorderingState.reachedIndex = index
reorderingState.draggingItemSize = size.height
nextItemSize = reorderingState.draggingItemSize
previousItemSize = -reorderingState.draggingItemSize
reorderingState.offset.updateBounds(
lowerBound = -index * reorderingState.draggingItemSize,
upperBound = (reorderingState.lastIndex - index) * reorderingState.draggingItemSize
)
},
{
coroutineScope.launch {
reorderingState.offset.animateTo((previousItemSize + nextItemSize) / 2)
withContext(Dispatchers.Main) {
onDragEnd?.invoke(reorderingState.reachedIndex)
}
if (reorderingState.areEquals(
reorderingState.draggingIndex,
reorderingState.reachedIndex
)
) {
reorderingState.draggingIndex = -1
reorderingState.reachedIndex = -1
reorderingState.draggingItemSize = 0
reorderingState.offset.snapTo(0)
}
}
},
{},
{ _, offset ->
val delta = when (orientation) {
Orientation.Vertical -> offset.y
Orientation.Horizontal -> offset.x
}.roundToInt()
val targetOffset = reorderingState.offset.value + delta
if (targetOffset > nextItemSize) {
if (reorderingState.reachedIndex < reorderingState.lastIndex) {
reorderingState.reachedIndex += 1
nextItemSize += reorderingState.draggingItemSize
previousItemSize += reorderingState.draggingItemSize
onMove?.invoke()
}
} else if (targetOffset < previousItemSize) {
if (reorderingState.reachedIndex > 0) {
reorderingState.reachedIndex -= 1
previousItemSize -= reorderingState.draggingItemSize
nextItemSize -= reorderingState.draggingItemSize
onMove?.invoke()
}
}
coroutineScope.launch {
reorderingState.offset.snapTo(targetOffset)
}
},
)
}
.offset {
when (orientation) {
Orientation.Vertical -> IntOffset(0, translation)
Orientation.Horizontal -> IntOffset(translation, 0)
}
}
.zIndex(if (reorderingState.draggingIndex == index) 1f else 0f)
}
fun Modifier.dragToReorder(
reorderingState: ReorderingState,
index: Int,
orientation: Orientation,
onDragStart: (() -> Unit)? = null,
onMove: (() -> Unit)? = null,
onDragEnd: ((Int) -> Unit)? = null
): Modifier = dragToReorder(
reorderingState = reorderingState,
index = index,
orientation = orientation,
function = PointerInputScope::detectDragGestures,
onDragStart = onDragStart,
onMove = onMove,
onDragEnd = onDragEnd,
)
fun Modifier.verticalDragToReorder(
reorderingState: ReorderingState,
index: Int,
onDragStart: (() -> Unit)? = null,
onMove: (() -> Unit)? = null,
onDragEnd: ((Int) -> Unit)? = null
): Modifier = dragToReorder(
reorderingState = reorderingState,
index = index,
orientation = Orientation.Vertical,
onDragStart = onDragStart,
onMove = onMove,
onDragEnd = onDragEnd,
)
fun Modifier.horizontalDragToReorder(
reorderingState: ReorderingState,
index: Int,
onDragStart: (() -> Unit)? = null,
onMove: (() -> Unit)? = null,
onDragEnd: ((Int) -> Unit)? = null
): Modifier = dragToReorder(
reorderingState = reorderingState,
index = index,
orientation = Orientation.Horizontal,
onDragStart = onDragStart,
onMove = onMove,
onDragEnd = onDragEnd,
)
fun Modifier.dragAfterLongPressToReorder(
reorderingState: ReorderingState,
index: Int,
orientation: Orientation,
onDragStart: (() -> Unit)? = null,
onMove: (() -> Unit)? = null,
onDragEnd: ((Int) -> Unit)? = null
): Modifier = dragToReorder(
reorderingState = reorderingState,
index = index,
orientation = orientation,
function = PointerInputScope::detectDragGesturesAfterLongPress,
onDragStart = onDragStart,
onMove = onMove,
onDragEnd = onDragEnd,
)
fun Modifier.verticalDragAfterLongPressToReorder(
reorderingState: ReorderingState,
index: Int,
onDragStart: (() -> Unit)? = null,
onMove: (() -> Unit)? = null,
onDragEnd: ((Int) -> Unit)? = null
): Modifier = dragAfterLongPressToReorder(
reorderingState = reorderingState,
index = index,
orientation = Orientation.Vertical,
onDragStart = onDragStart,
onMove = onMove,
onDragEnd = onDragEnd,
)
fun Modifier.horizontalDragAfterLongPressToReorder(
reorderingState: ReorderingState,
index: Int,
onDragStart: (() -> Unit)? = null,
onMove: (() -> Unit)? = null,
onDragEnd: ((Int) -> Unit)? = null
): Modifier = dragAfterLongPressToReorder(
reorderingState = reorderingState,
index = index,
orientation = Orientation.Horizontal,
onDragStart = onDragStart,
onMove = onMove,
onDragEnd = onDragEnd,
)