Evian Pringle
Evian Pringle

Reputation: 45

How to drag and drop an image from a Recycler view and place it outside of it into another layout

I have a horizontal Recylcer view which holds a list of items. Each item has a text view and image view to represent the item (e.g an image of a square and the text "Square" above the image). The recylcer view is inside of it's own relative layout positioned at the top of the screen. What I need to do is drag an image from the recycler view and drop it outside of it into another layout that is below this one. However, the image that is dragged from the recycler view must not be removed, but instead a copy of the image should be placed into the outside layout. The image must also be dropped in the exact position in the layout where the user releases their finger. Is there any way to achieve this?

An example of this could be like in a game, where the user has a list of objects to drag and drop into the game world.

Here's an example to illustrate what I mean:

example

Adapter class for Recycler View:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {

private List<ListItems> listItems;
private Context context;

public RecyclerAdapter(List<ListItems> listItems, Context context) {
    this.listItems = listItems;
    this.context = context;
}

@Override
public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.layout_recycler_view_row_one, parent, false);

    ViewHolder vh = new ViewHolder(v);
    return vh;
}

@Override
public void onBindViewHolder(RecyclerAdapter.ViewHolder holder, int position) {

    ListItems listItems = listItems.get(position);

    holder.textView.setText(listItems.getItemName());
    holder.imageView.setImageResource(listItems.getImageDrawable());
}


@Override
public int getItemCount() {
    return listItems.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {

    public ImageView imageView;
    public TextView textView;

    public ViewHolder(View itemView) {
        super(itemView);
        imageView = (ImageView) itemView.findViewById(R.id.item_image_view);
        textView = (TextView) itemView.findViewById(R.id.item_name_text_view);

    }
}

MainActivity.class:

 public class MainActivity extends AppCompatActivity {

RelativeLayout relativeLayoutMiddle;

private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private RecyclerView.Adapter adapter;
private List<ListItems> listItems;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    listItems = new ArrayList<>();

    for (int i = 0; i < 4; i++) {
        if(i==0)
        {
            ListItems listItems = new ListItems("Square", R.drawable.square);
            listItems.add(listItems);
        }
        else if(i==1)
        {
            ListItems listItems = new ListItems("Circle", R.drawable.circle);
            listItems.add(listItems);
        }
        else if(i==2)
        {
            ListItems listItems = new ListItems("Rectangle", R.drawable.rectangle);
            listItems.add(listItems);
        }
        else if(i==3)
        {
            ListItems listItems = new ListItems("Triangle", R.drawable.triangle);
            listItems.add(listItems);
        }
    }

    recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    recyclerView.setHasFixedSize(true);
    layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
    recyclerView.setLayoutManager(layoutManager);
    adapter = new RecyclerAdapter(listItems, this);
    recyclerView.setAdapter(adapter);

    relativeLayoutMiddle = (RelativeLayout) findViewById(R.id.relativeLayoutMiddle);

Upvotes: 3

Views: 3959

Answers (1)

adneal
adneal

Reputation: 30794

This can basically be accomplished in just a few lines of code using the Android drag and drop api.

  1. Call View.startDragAndDrop
  2. Attach a View.OnDragListener to your drop container
  3. Wait for DragEvent.ACTION_DROP and then inflate and position your shape

In your RecyclerView.Adapter, attach a View.OnLongClickListener and then call View.startDragAndDrop. Something like:

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    final View v = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.layout_recycler_view_row_one, parent, false);
    final ViewHolder holder = new ViewHolder(v);
    final View shape = holder.imageView;
    holder.itemView.setOnLongClickListener(v -> {
        final ListItems item = listItems.get(holder.getAdapterPosition());
        final DragData state = new DragData(item, shape.getWidth(), shape.getHeight());
        final DragShadowBuilder shadow = new DragShadowBuilder(shape);
        ViewCompat.startDragAndDrop(shape, null, shadow, state, 0);
        return true;
    });
    return holder;
}

public class DragData {

    public final ListItems item;
    public final int width;
    public final int height;

    public DragData(ListItems item, int width, int height) {
        this.item= item;
        this.width = width;
        this.height = height;
    }

}

In your Activity or wherever you inflate your bottom container layout, call View.setOnDragListener and when DragEvent.ACTION_DROP is called we can inflate a copy of the View you called startDragAndDrop on. Something like:

@Override
public boolean onDrag(View v, DragEvent event) {
    switch (event.getAction()) {
        case DragEvent.ACTION_DRAG_ENTERED:
            dropContainer.setBackgroundColor(GREEN);
            break;
        case DragEvent.ACTION_DRAG_EXITED:
            dropContainer.setBackgroundColor(RED);
            break;
        case DragEvent.ACTION_DRAG_ENDED:
            dropContainer.setBackgroundColor(WHITE);
            break;
        case DragEvent.ACTION_DROP:
            final float dropX = event.getX();
            final float dropY = event.getY();
            final DragData state = (DragData) event.getLocalState();

            final ImageView shape = (ImageView) LayoutInflater.from(this).inflate(
                    R.layout.view_shape, dropContainer, false);
            shape.setImageResource(state.item.getImageDrawable());
            shape.setX(dropX - (float) state.width / 2);
            shape.setY(dropY - (float) state.height / 2);
            shape.getLayoutParams().width = state.width;
            shape.getLayoutParams().height = state.height;
            dropContainer.addView(shape);
            break;
        default:
            break;
    }
    return true;
}

Results

Upvotes: 9

Related Questions