Я разрабатываю тестовое приложение с помощью Jetpack Compose, используя функции перетаскивания. У меня есть три задачи, которые указаны в каждом назначенном столбце: «Сделать», «В процессе» и «Готово». Когда я пытаюсь переместить любую задачу в другой столбец, она меняется, но затем возвращается в исходный столбец. В чем может быть проблема?
Вот мой код на GitHub: репозиторий GitHub
DragAndDrop.kt
internal val LocalDragTargetIfo = compositionLocalOf{ DragTargetInfo() }
@Composable
fun DragableScreen(modifier: Modifier = Modifier,content: @Composable BoxScope.() -> Unit){
val state= remember {
DragTargetInfo()
}
CompositionLocalProvider(LocalDragTargetIfo provides state){
Box(modifier=Modifier.fillMaxSize()){
content()
if(state.isDagging){
var targetSize by remember {
mutableStateOf(IntSize.Zero)
}
Box(
modifier = Modifier
.graphicsLayer {
val offset = (state.dragPosition + state.dragOffset)
scaleX = 1.3f
scaleY = 1.3f
alpha = if (targetSize == IntSize.Zero) 0f else .9f
translationX = offset.x.minus(targetSize.width / 2)
translationY = offset.y.minus(targetSize.height / 2)
}
.onGloballyPositioned {
targetSize = it.size
}
){
state.draggableComposable?.invoke()
}
}
}
}
}
@Composable
fun DragTrarget(
modifier: Modifier = Modifier,
datatoDrop: T,
viewModel: MainViewModel,
content: @Composable () -> Unit
) {
var currentPosition by remember { mutableStateOf(Offset.Zero) }
val currentState = LocalDragTargetIfo.current
Box(
modifier = modifier
.onGloballyPositioned { currentPosition = it.localToWindow(Offset.Zero) }
.pointerInput(Unit) {
detectDragGesturesAfterLongPress(
onDragStart = {
viewModel.startDragging()
currentState.dataToDrop = datatoDrop
currentState.isDagging = true
currentState.dragPosition = currentPosition + it
currentState.draggableComposable = content
},
onDrag = { change, dragAmount ->
change.consume()
currentState.dragOffset += Offset(dragAmount.x, dragAmount.y)
},
onDragEnd = {
viewModel.stopDragging()
currentState.dragOffset = Offset.Zero
currentState.isDagging = false
},
onDragCancel = {
viewModel.stopDragging()
currentState.dragOffset = Offset.Zero
currentState.isDagging = false
}
)
}
) {
content()
}
}
@Composable
fun DropItem(
modifier: Modifier,
content: @Composable BoxScope.(isInBound: Boolean, data: T?) -> Unit
) {
val dragInfo = LocalDragTargetIfo.current
val dragPosition = dragInfo.dragPosition
val dragOffset = dragInfo.dragOffset
var isCurretDropTarget by remember { mutableStateOf(false) }
Box(
modifier = modifier
.background(Color.Red)
.onGloballyPositioned {
it.boundsInWindow().let { rect ->
isCurretDropTarget = rect.contains(dragPosition + dragOffset)
}
}
) {
val data = if (isCurretDropTarget && !dragInfo.isDagging) {
dragInfo.dataToDrop as T?
} else {
null
}
// Solo ejecuta el contenido si el arrastre ha terminado y está en el área de soltar
if (!dragInfo.isDagging && isCurretDropTarget && data != null) {
content(true, data)
} else {
content(false, null)
}
}
}
internal class DragTargetInfo {
var isDagging:Boolean by mutableStateOf(false)
var dragPosition by mutableStateOf(Offset.Zero)
var dragOffset by mutableStateOf(Offset.Zero)
var draggableComposable by mutableStateOf Unit)?>(null)
var dataToDrop by mutableStateOf(null)
}
MainViewModel.kt
class MainViewModel : ViewModel() {
var isCurrentlyDragging by mutableStateOf(false)
private set
val items = mutableStateListOf()
var addedTasks = mutableStateListOf()
private set
fun addExampleTasks() {
items.addAll(listOf(
Task(1, "Task 1","Description 1", TaskStatus.TO_DO),
Task(2, "Task 2","Description 2", TaskStatus.IN_PROGRESS),
Task(3, "Task 3","Description 3", TaskStatus.DONE)
))
}
fun startDragging() {
isCurrentlyDragging = true
}
fun stopDragging() {
isCurrentlyDragging = false
}
fun addTask(task: Task) {
items.add(task)
}
fun updateTaskStatus(task: Task, newStatus: TaskStatus) {
val index = items.indexOfFirst { it.id == task.id }
if (index != -1) {
val currentTask = items[index]
if (currentTask.status != newStatus) {
println("Updating task ${task.id} from ${currentTask.status} to $newStatus")
// Actualizar el estado directamente
items[index] = currentTask.copy(status = newStatus)
}
}
}
}
MainScreen.kt
@Composable
fun MainScreen(mainViewModel: MainViewModel = viewModel()) {
// Observar directamente el estado de items
val tasks = mainViewModel.items
// Llamar a un método para añadir tareas de ejemplo (solo si es necesario)
LaunchedEffect(Unit) {
if (tasks.isEmpty()) {
mainViewModel.addExampleTasks()
}
}
Row(modifier = Modifier.fillMaxSize()) {
TaskColumn(
title = "To Do",
tasks = tasks.filter { it.status == TaskStatus.TO_DO },
onTaskDropped = { task ->
mainViewModel.updateTaskStatus(task, TaskStatus.TO_DO)
},
viewModel = mainViewModel,
modifier = Modifier.weight(1f)
)
TaskColumn(
title = "In Progress",
tasks = tasks.filter { it.status == TaskStatus.IN_PROGRESS },
onTaskDropped = { task ->
mainViewModel.updateTaskStatus(task, TaskStatus.IN_PROGRESS)
},
viewModel = mainViewModel,
modifier = Modifier.weight(1f)
)
TaskColumn(
title = "Done",
tasks = tasks.filter { it.status == TaskStatus.DONE },
onTaskDropped = { task ->
mainViewModel.updateTaskStatus(task, TaskStatus.DONE)
},
viewModel = mainViewModel,
modifier = Modifier.weight(1f)
)
}
}
@Composable
fun TaskColumn(
title: String,
tasks: List,
onTaskDropped: (Task) -> Unit,
viewModel: MainViewModel, // Asegúrate de recibir el ViewModel
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.fillMaxHeight()
.padding(8.dp)
.border(1.dp, Color.Black, shape = RoundedCornerShape(8.dp))
) {
Text(title, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(8.dp))
tasks.forEach { task ->
DragTrarget(
modifier = Modifier.wrapContentWidth().padding(4.dp),
datatoDrop = task,
viewModel = viewModel // Pasar el ViewModel aquí
) {
TaskItem(task)
}
}
DropItem(
modifier = Modifier.fillMaxSize()
) { isInBound, droppedTask ->
if (isInBound && droppedTask != null) {
println("Task dropped: ${droppedTask.title} to $title")
onTaskDropped(droppedTask)
}
}
}
}
@Composable
fun TaskItem(task: Task) {
Box(
modifier = Modifier
.wrapContentWidth()
.padding(8.dp)
.background(Color.LightGray, shape = RoundedCornerShape(8.dp)) // Aquí se aplica el radio
.border(1.dp, Color.LightGray, shape = RoundedCornerShape(8.dp)) // Asegúrate de aplicar el mismo shape al borde
.padding(8.dp)
) {
Column {
Text("${task.title}:")
Text("${task.description}")
}
}
}
Task.kt
data class Task (val id: Int, val title: String, val description: String, var status: TaskStatus)
enum class TaskStatus{
TO_DO,IN_PROGRESS,DONE
}
Подробнее здесь: https://stackoverflow.com/questions/791 ... how-to-fix
Jetpack Compose: задачи перетаскивания возвращаются в исходный столбец — как исправить? ⇐ Android
Форум для тех, кто программирует под Android
1732098023
Anonymous
Я разрабатываю тестовое приложение с помощью Jetpack Compose, используя функции перетаскивания. У меня есть три задачи, которые указаны в каждом назначенном столбце: «Сделать», «В процессе» и «Готово». Когда я пытаюсь переместить любую задачу в другой столбец, она меняется, но затем возвращается в исходный столбец. В чем может быть проблема?
Вот мой код на GitHub: репозиторий GitHub
DragAndDrop.kt
internal val LocalDragTargetIfo = compositionLocalOf{ DragTargetInfo() }
@Composable
fun DragableScreen(modifier: Modifier = Modifier,content: @Composable BoxScope.() -> Unit){
val state= remember {
DragTargetInfo()
}
CompositionLocalProvider(LocalDragTargetIfo provides state){
Box(modifier=Modifier.fillMaxSize()){
content()
if(state.isDagging){
var targetSize by remember {
mutableStateOf(IntSize.Zero)
}
Box(
modifier = Modifier
.graphicsLayer {
val offset = (state.dragPosition + state.dragOffset)
scaleX = 1.3f
scaleY = 1.3f
alpha = if (targetSize == IntSize.Zero) 0f else .9f
translationX = offset.x.minus(targetSize.width / 2)
translationY = offset.y.minus(targetSize.height / 2)
}
.onGloballyPositioned {
targetSize = it.size
}
){
state.draggableComposable?.invoke()
}
}
}
}
}
@Composable
fun DragTrarget(
modifier: Modifier = Modifier,
datatoDrop: T,
viewModel: MainViewModel,
content: @Composable () -> Unit
) {
var currentPosition by remember { mutableStateOf(Offset.Zero) }
val currentState = LocalDragTargetIfo.current
Box(
modifier = modifier
.onGloballyPositioned { currentPosition = it.localToWindow(Offset.Zero) }
.pointerInput(Unit) {
detectDragGesturesAfterLongPress(
onDragStart = {
viewModel.startDragging()
currentState.dataToDrop = datatoDrop
currentState.isDagging = true
currentState.dragPosition = currentPosition + it
currentState.draggableComposable = content
},
onDrag = { change, dragAmount ->
change.consume()
currentState.dragOffset += Offset(dragAmount.x, dragAmount.y)
},
onDragEnd = {
viewModel.stopDragging()
currentState.dragOffset = Offset.Zero
currentState.isDagging = false
},
onDragCancel = {
viewModel.stopDragging()
currentState.dragOffset = Offset.Zero
currentState.isDagging = false
}
)
}
) {
content()
}
}
@Composable
fun DropItem(
modifier: Modifier,
content: @Composable BoxScope.(isInBound: Boolean, data: T?) -> Unit
) {
val dragInfo = LocalDragTargetIfo.current
val dragPosition = dragInfo.dragPosition
val dragOffset = dragInfo.dragOffset
var isCurretDropTarget by remember { mutableStateOf(false) }
Box(
modifier = modifier
.background(Color.Red)
.onGloballyPositioned {
it.boundsInWindow().let { rect ->
isCurretDropTarget = rect.contains(dragPosition + dragOffset)
}
}
) {
val data = if (isCurretDropTarget && !dragInfo.isDagging) {
dragInfo.dataToDrop as T?
} else {
null
}
// Solo ejecuta el contenido si el arrastre ha terminado y está en el área de soltar
if (!dragInfo.isDagging && isCurretDropTarget && data != null) {
content(true, data)
} else {
content(false, null)
}
}
}
internal class DragTargetInfo {
var isDagging:Boolean by mutableStateOf(false)
var dragPosition by mutableStateOf(Offset.Zero)
var dragOffset by mutableStateOf(Offset.Zero)
var draggableComposable by mutableStateOf Unit)?>(null)
var dataToDrop by mutableStateOf(null)
}
MainViewModel.kt
class MainViewModel : ViewModel() {
var isCurrentlyDragging by mutableStateOf(false)
private set
val items = mutableStateListOf()
var addedTasks = mutableStateListOf()
private set
fun addExampleTasks() {
items.addAll(listOf(
Task(1, "Task 1","Description 1", TaskStatus.TO_DO),
Task(2, "Task 2","Description 2", TaskStatus.IN_PROGRESS),
Task(3, "Task 3","Description 3", TaskStatus.DONE)
))
}
fun startDragging() {
isCurrentlyDragging = true
}
fun stopDragging() {
isCurrentlyDragging = false
}
fun addTask(task: Task) {
items.add(task)
}
fun updateTaskStatus(task: Task, newStatus: TaskStatus) {
val index = items.indexOfFirst { it.id == task.id }
if (index != -1) {
val currentTask = items[index]
if (currentTask.status != newStatus) {
println("Updating task ${task.id} from ${currentTask.status} to $newStatus")
// Actualizar el estado directamente
items[index] = currentTask.copy(status = newStatus)
}
}
}
}
MainScreen.kt
@Composable
fun MainScreen(mainViewModel: MainViewModel = viewModel()) {
// Observar directamente el estado de items
val tasks = mainViewModel.items
// Llamar a un método para añadir tareas de ejemplo (solo si es necesario)
LaunchedEffect(Unit) {
if (tasks.isEmpty()) {
mainViewModel.addExampleTasks()
}
}
Row(modifier = Modifier.fillMaxSize()) {
TaskColumn(
title = "To Do",
tasks = tasks.filter { it.status == TaskStatus.TO_DO },
onTaskDropped = { task ->
mainViewModel.updateTaskStatus(task, TaskStatus.TO_DO)
},
viewModel = mainViewModel,
modifier = Modifier.weight(1f)
)
TaskColumn(
title = "In Progress",
tasks = tasks.filter { it.status == TaskStatus.IN_PROGRESS },
onTaskDropped = { task ->
mainViewModel.updateTaskStatus(task, TaskStatus.IN_PROGRESS)
},
viewModel = mainViewModel,
modifier = Modifier.weight(1f)
)
TaskColumn(
title = "Done",
tasks = tasks.filter { it.status == TaskStatus.DONE },
onTaskDropped = { task ->
mainViewModel.updateTaskStatus(task, TaskStatus.DONE)
},
viewModel = mainViewModel,
modifier = Modifier.weight(1f)
)
}
}
@Composable
fun TaskColumn(
title: String,
tasks: List,
onTaskDropped: (Task) -> Unit,
viewModel: MainViewModel, // Asegúrate de recibir el ViewModel
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.fillMaxHeight()
.padding(8.dp)
.border(1.dp, Color.Black, shape = RoundedCornerShape(8.dp))
) {
Text(title, style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(8.dp))
tasks.forEach { task ->
DragTrarget(
modifier = Modifier.wrapContentWidth().padding(4.dp),
datatoDrop = task,
viewModel = viewModel // Pasar el ViewModel aquí
) {
TaskItem(task)
}
}
DropItem(
modifier = Modifier.fillMaxSize()
) { isInBound, droppedTask ->
if (isInBound && droppedTask != null) {
println("Task dropped: ${droppedTask.title} to $title")
onTaskDropped(droppedTask)
}
}
}
}
@Composable
fun TaskItem(task: Task) {
Box(
modifier = Modifier
.wrapContentWidth()
.padding(8.dp)
.background(Color.LightGray, shape = RoundedCornerShape(8.dp)) // Aquí se aplica el radio
.border(1.dp, Color.LightGray, shape = RoundedCornerShape(8.dp)) // Asegúrate de aplicar el mismo shape al borde
.padding(8.dp)
) {
Column {
Text("${task.title}:")
Text("${task.description}")
}
}
}
Task.kt
data class Task (val id: Int, val title: String, val description: String, var status: TaskStatus)
enum class TaskStatus{
TO_DO,IN_PROGRESS,DONE
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79199873/jetpack-compose-drag-and-drop-tasks-return-to-original-column-how-to-fix[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия