androiddrag-and-dropandroid-gridlayoutreorderable-list

Android drag and drop on the same view results in reporting drop result: false


I am trying to make a reorderable GridLayout with drag and drop.

Basically, when I drag something in the GridLayout, it should reorder the GridLayout based on the dragging.

I have this GridLayout which has 6 FrameLayout inside.

<GridLayout
    android:id="@+id/myGridLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:columnCount="3">

    <FrameLayout
        android:background="@android:color/holo_blue_dark"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_columnWeight="1">
    </FrameLayout>

    <FrameLayout
        android:background="@android:color/black"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_columnWeight="1">
    </FrameLayout>

    <FrameLayout
        android:background="@android:color/darker_gray"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_columnWeight="1">
    </FrameLayout>

    <FrameLayout
        android:background="@android:color/holo_green_dark"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_columnWeight="1">
    </FrameLayout>

    <FrameLayout
        android:background="@android:color/holo_purple"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_columnWeight="1">
    </FrameLayout>

    <FrameLayout
        android:background="@android:color/holo_red_dark"
        android:layout_width="0dp"
        android:layout_height="100dp"
        android:layout_columnWeight="1">
    </FrameLayout>

</GridLayout>

So It looks like the screenshot below

enter image description here

I have registered all the FrameLayouts in the GridLayout for LongClickListner and DragListener as per the codes below

    for (i in 0 until myGridLayout.childCount) {
        val childView = myGridLayout.getChildAt(i)

        childView.setOnLongClickListener {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                it.visibility = View.INVISIBLE
                it.startDragAndDrop(null, CustomDragShadowBuilder(it, lastTouch), it, 0)
            } else it.startDrag(null, CustomDragShadowBuilder(it, lastTouch), it, 0)
            true
        }


        childView.setOnDragListener { v, event ->
            when (event.action) {
                DragEvent.ACTION_DRAG_STARTED -> true
                DragEvent.ACTION_DRAG_ENTERED -> {
                        val draggingView = event.localState as View
                        val dragEnteredIndex = myGridLayout.indexOfChild(v)
                        val draggingViewIndex = myGridLayout.indexOfChild(draggingView)
                        myGridLayout.removeViewAt(draggingViewIndex)
                        myGridLayout.addView(draggingView, dragEnteredIndex)
                        true
                }
                DragEvent.ACTION_DRAG_LOCATION -> true
                DragEvent.ACTION_DRAG_EXITED -> true
                DragEvent.ACTION_DROP -> true
                DragEvent.ACTION_DRAG_ENDED -> {
                    val draggingView = event.localState as View
                    draggingView.post { run { draggingView.visibility = View.VISIBLE } }
                    true
                }
                else -> false
            }
        }
    }

So, when you drag the red FrameLayout over to gray FrameLayout, the DragEvent.ACTION_DRAG_ENTERED is called. Then, I just remove the red FrameLayout and add it to the index of the gray FrameLayout where the DragShadow is over, so that I can reorder the GridLayout in real time. So, when I move around a FrameLayout in the GridLayout, I have got something like this, which is what I expect.

enter image description here

But, as you can see, when I release the drag, the red FrameLayout, or DragShadow goes back to its original position and console says I/ViewRootImpl[MainActivity]: Reporting drop result: false. Finally, the DragEvent.ACTION_DRAG_ENDED is called and make the red FrameLayout visible.

So, here are questions,

  1. why the drag fails? Is this because I release the drag over the same FrameLayout, which is red one please?

  2. Is there any way that I can prevent the DragShadow from getting back to its original position? What I want is just that when I release my drag, I just want the FrameLayout to appear at the new position, not getting back to its initial position.

Thank you guys in advance.


Solution

  • For those who have the same issues, I solved this isseus by using recyclerview with ItemTouchHelper which does have drag and drop featuers. You don't have to do your own codes.