Search code examples
javaandroidandroid-recyclerviewdrag-and-dropz-order

how can i make the dragged item from a recyclerview appear over the target and not disappear underneath it?


I have a recyclerview and an imageview. I want to be able to drag an item from the list over the top of the imageview (and later on drop it). The problem is that when i drag the item it goes underneath the image not over it.

section from activity

private void updateRecycler(RecyclerView myrecyclerview)
    {
        RecyclerView r = myrecyclerview;

        itemsAdapter=new skutest_recyclerview_viewadapter(itmlist);

        runOnUiThread(() -> {
            if (r.getLayoutManager()==null)
            {
                r.setLayoutManager(new LinearLayoutManager(MainActivity.this));
            }
            r.setAdapter(itemsAdapter);

            ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback(itemsAdapter));
            itemTouchHelper.attachToRecyclerView(r);

        });
    }

public interface ItemTouchHelperAdapter {

    void onItemMoved(int fromPosition, int toPosition);

    void onItemDropped(int position); // Optional for handling dropped items on image
}
public class skutest_recyclerview_viewadapter extends RecyclerView.Adapter<skutest_recyclerview_viewadapter.ViewHolder> implements ItemTouchHelperAdapter
{
    RecyclerView mRecyclerView;
    List<cl_sku_info> skulist;

    public skutest_recyclerview_viewadapter(List<cl_sku_info> skulist)
    {
        this.skulist=skulist;

    }

    @NonNull
    @Override
    public skutest_recyclerview_viewadapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
    {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        ItemBinding b = ItemBinding.inflate(inflater, parent, false);
        return new ViewHolder(b);
    }

    @Override
    public void onBindViewHolder(@NonNull skutest_recyclerview_viewadapter.ViewHolder holder, int position)
    {
        holder.setData(skulist.get(position));

    }

    @Override
    public int getItemCount()
    {
        return skulist==null ? 0 : skulist.size();
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView)
    {
        super.onAttachedToRecyclerView(recyclerView);
        mRecyclerView = recyclerView;
    }

    @Override
    public void onItemMoved(int fromPosition, int toPosition)
    {

    }

    @Override
    public void onItemDropped(int position)
    {

    }


    public static class ViewHolder extends RecyclerView.ViewHolder
    {
        private final ItemBinding binding;
        public ViewHolder(ItemBinding binding)
        {
            super(binding.getRoot());
            this.binding=binding;
        }

        public void setData(cl_sku_info clSkuInfo)
        {
            binding.label.setText("*****");//clSkuInfo.getSTOCK_NO());
            binding.image.setImageBitmap(images.getInstance().getimage(clSkuInfo.getSTOCK_NO(),100));
        }

    }


}

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

public class ItemTouchHelperCallback extends ItemTouchHelper.Callback
{
    private final ItemTouchHelperAdapter adapter;

    public ItemTouchHelperCallback(ItemTouchHelperAdapter adapter)
    {
        this.adapter = adapter;
    }


    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder)
    {
        final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT;
        final int swipeFlags = 0; // Disable swiping for now
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target)
    {
        adapter.onItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction)
    {

    }
}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:weightSum="2"
    android:hardwareAccelerated="false"
    tools:context=".activities.MainActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/myrecyclerview"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        tools:itemCount="20"
        android:layout_weight="1"
        tools:listitem="@layout/item" />


    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"

        android:background="@drawable/i1"
        android:scaleType="center" />
</LinearLayout>

enter image description here

enter image description here

As you can see in the images the dragged item is getting lost underneath the target image. I thought that the default behaviour was to float above everything and I can't see where I've gone wrong.


Solution

  • It turns out i had taken the wrong approach.

    The issue is resolved by discarding the

    ItemTouchHelperCallback and ItemTouchHelperAdapter

    getting rid of

                ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback(itemsAdapter));
                itemTouchHelper.attachToRecyclerView(r);
    

    changing

        @SuppressLint("ClickableViewAccessibility")
        @Override
        public void onBindViewHolder(@NonNull skutest_recyclerview_viewadapter.ViewHolder holder, int position)
        {
            holder.setData(skulist.get(position));
    
            holder.getBinding().getRoot().setOnTouchListener(new View.OnTouchListener()
            {
                @Override
                public boolean onTouch(View v, MotionEvent event)
                {
                    return tevent(v,event);
                }
            });
            //holder.getBinding().getRoot().setOnDragListener(new DragListener());
        }
    

    and adding below (credits https://github.com/jkozh/DragDropTwoRecyclerViews)

    public boolean tevent(View v,MotionEvent event)
        {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    ClipData data = ClipData.newPlainText("", "");
                    View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
                    v.startDragAndDrop(data, shadowBuilder, v, 0);
                    v.performClick();
                    return true;
            }
            return false;
        }
    

    There was a large amount of quagmire to get to this understanding. The default drag and drop functionality of the recyclerview appears to be intended for operations withing the same recyclerview such as re-ordering. When you want the drag to escape the recyclerview and float over everything you have to do something like this.