Search code examples
androidkotlinandroid-recyclerviewdrag-and-dropdrag-event

Need to implement drag and drop of an item from one recyclerview to another recyclerview. Android, kotlin


I had problems with implementation via ItemTouchHelper, then I found in stackover, this is the solution Drag and Drop between two RecyclerView Rewrote it on kotlin for testing to figure out how to do in my app - here's my github - https://github.com/Avdors/RecyclerDragAndDrop here's the java original, 6 years old - https://github.com/jkozh/DragDropTwoRecyclerViews But can't solve the problem, drag and drop works but only if I drag an item from the top to another item. If I drag into an empty space or into an empty recyclerview, it doesn't work, and I want the item to be moved to the end of the list. Can anyone tell me how to solve this?

I tried to look at other DragEvent, DragEvent.ACTION_DRAG_EXITED works when releasing an item, but it works when releasing and taking an object to drag, 4 times in a row. And if you don't put an element above another element ACTION_DROP doesn't work, in the project Log to see where it starts. I thought as an option to fill recycler with invisible elements, but it seems there should be a simpler solution. Here's the code for the basic implementation:`class DragListener(private val listener: Listener) : View.OnDragListener {

private var isDropped = false

override fun onDrag(v: View, event: DragEvent): Boolean {
    when (event.action) {
       // DragEvent.ACTION_DROP,
       // DragEvent.ACTION_DRAG_EXITED
                DragEvent.ACTION_DROP -> {
            Log.d("MyLog", "ACTION_DROP")
            isDropped = true
            var positionTarget: Int

            val viewSource = event.localState as? View
            val viewId = v.id
            val flItem = R.id.frame_layout_item
            val tvEmptyListTop = R.id.tvEmptyListTop
            val tvEmptyListBottom = R.id.tvEmptyListBottom
            val rvTop = R.id.rvTop
            val rvBottom = R.id.rvBottom
            Log.d("MyLog", "viewId $viewId")
            when (viewId) {
                flItem, tvEmptyListTop, tvEmptyListBottom, rvTop, rvBottom -> {
                    Log.d("MyLog", "flItem, tvEmptyListTop, tvEmptyListBottom, rvTop, rvBottom")
                    val target: RecyclerView = when (viewId) {
                        tvEmptyListTop, rvTop -> v.rootView.findViewById(rvTop)
                        tvEmptyListBottom, rvBottom -> v.rootView.findViewById(rvBottom)
                        else -> v.parent as RecyclerView
                    }

                    positionTarget = v.tag as Int
                    Log.d("MyLog", "positionTarget = $positionTarget")

                    Log.d("MyLog", "viewSource $viewSource")
                    if (viewSource != null) {
                        val source = viewSource.parent as RecyclerView
                        val adapterSource = source.adapter as ListAdapter
                        val positionSource = viewSource.tag as Int
                        Log.d("MyLog", "positionSource $positionSource")
                        val list = adapterSource.getList()[positionSource]
                        val listSource = adapterSource.getList().toMutableList()

                        listSource.removeAt(positionSource)
                        adapterSource.updateList(listSource)
                        adapterSource.notifyDataSetChanged()

                        val adapterTarget = target.adapter as ListAdapter
                        Log.d("MyLog", "target $target")
                        val customListTarget = adapterTarget.getList().toMutableList()
                        Log.d("MyLog", "customListTarget $customListTarget")
                        if (positionTarget < 0) {
                            customListTarget.add(list)
                          //  customListTarget.add(positionTarget, list)
                        } else {
                            customListTarget.add(positionTarget, list)
                        }
                        adapterTarget?.updateList(customListTarget)
                        adapterTarget?.notifyDataSetChanged()
                    }
                }
            }
        }

        DragEvent.ACTION_DRAG_EXITED -> {
            Log.d("MyLog", "Enter ACTION_DRAG_EXITED")
        }
    }

    if (!isDropped && event.localState != null) {
        (event.localState as? View)?.visibility = View.VISIBLE
    }
    return true
}

}`


Solution

  • Everything worked out, I need to put listeners for recyclerview. otherwise DragEvent.ACTION_DROP doesn't work when releasing an element on another recyclerview. I have updated the code in Github, maybe someone will need a more recent working example in kotlin. https://github.com/Avdors/RecyclerDragAndDrop In my activity, I add:

    tvEmptyListTop.setOnDragListListener(topListAdapter.getDragInstance())
    rvTop.setOnDragListListener(topListAdapter.getDragInstance()).
    

    and

    tvEmptyListBottom.setOnDragListListListener(bottomListAdapter.getDragInstance())
            rvBottom.setOnDragListListener(bottomListAdapter.getDragInstance())
    

    here's all MainActivity:

    class MainActivity : AppCompatActivity(), Listener {
    private lateinit var rvTop: RecyclerView
    private lateinit var rvBottom: RecyclerView
    private lateinit var tvEmptyListTop: TextView
    private lateinit var tvEmptyListBottom: TextView
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    
        rvTop = findViewById(R.id.rvTop)
        rvBottom = findViewById(R.id.rvBottom)
        tvEmptyListTop = findViewById(R.id.tvEmptyListTop)
        tvEmptyListBottom = findViewById(R.id.tvEmptyListBottom)
    
        initTopRecyclerView()
        initBottomRecyclerView()
    
        tvEmptyListTop.visibility = View.GONE
        tvEmptyListBottom.visibility = View.GONE
    }
    
    override fun setEmptyListTop(visibility: Boolean) {
        tvEmptyListTop.visibility = if (visibility) View.VISIBLE else View.GONE
    }
    
    override fun setEmptyListBottom(visibility: Boolean) {
        tvEmptyListBottom.visibility = if (visibility) View.VISIBLE else View.GONE
    }
    private fun initTopRecyclerView() {
        rvTop.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
        val topList = listOf("A", "B")
        val topListAdapter = ListAdapter(topList, this)
        rvTop.adapter = topListAdapter
    
        tvEmptyListTop.setOnDragListener(topListAdapter.getDragInstance())
        rvTop.setOnDragListener(topListAdapter.getDragInstance())
    }
    
    private fun initBottomRecyclerView() {
        rvBottom.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
        val bottomList = listOf("C", "D")
        val bottomListAdapter = ListAdapter(bottomList, this)
        rvBottom.adapter = bottomListAdapter
        tvEmptyListBottom.setOnDragListener(bottomListAdapter.getDragInstance())
        rvBottom.setOnDragListener(bottomListAdapter.getDragInstance())
    }
    

    }