3

I have a RecyclerView whose layout is a GridLayout of 3 columns. I am using ItemTouchHelper to drag and drop in the GridLayout of the RecyclerView. In my Adapter there is a method onItemMove() which notify me the items which i have moved. It only swaps those items in the ArrayList which I have dragged and dropped. But due to the drag and drop there is a shift in other elements as well. I want to update their positions as well in the ArrayList.

Here is the Adapter of my Recyclerview:

public class GridAdapter extends RecyclerView.Adapter<GridAdapter.MyViewHolder> implements com.sagar.quizdemo.helper.ItemTouchHelperAdapter {
    String[] str = {"Level 1", "Level 2", "Level 3", "Level 4", "Level 5", "Level 6", "Level 7", "Level 8", "Level 9"};
    List<String> itemList, actualList;
    LayoutInflater inflater;
    Context context;
    private final OnStartDragListener mDragStartListener;

    public GridAdapter(Context context, OnStartDragListener dragStartListener) {
        this.context = context;
        mDragStartListener = dragStartListener;
        inflater = LayoutInflater.from(context);
        itemList = new ArrayList<>();
        actualList = new ArrayList<>(Arrays.asList(str));
    }

    public void getItemList(List<String> nameList) {
        int currentSize = itemList.size();
        itemList.clear();
        itemList.addAll(nameList);
        notifyItemRangeRemoved(0, currentSize);
        notifyItemRangeInserted(0, nameList.size());
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.custom_level_row, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        holder.textView.setText(itemList.get(position));
        // Start a drag whenever the handle view it touched
        holder.cardView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
                    mDragStartListener.onStartDrag(holder);
                }
                return false;
            }
        });
    }

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

    @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        Collections.swap(itemList, fromPosition, toPosition);
        notifyItemMoved(fromPosition, toPosition);
        notifyItemChanged(fromPosition);
        notifyItemChanged(toPosition);
        return true;
    }

    @Override
    public void onItemDismiss(int position) {
        itemList.remove(position);
        notifyItemRemoved(position);
    }

    public class MyViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {

        CardView cardView;
        TextView textView;

        public MyViewHolder(View itemView) {
            super(itemView);
            cardView = (CardView) itemView.findViewById(R.id.gameCard);
            textView = (TextView) itemView.findViewById(R.id.gameText);
        }

        @Override
        public void onItemSelected() {
            itemView.setBackgroundColor(Color.LTGRAY);
        }

        @Override
        public void onItemClear() {
            itemView.setBackgroundColor(Color.WHITE);
        }
    }
}

if initially the list is :

{"Level 1", "Level 2", "Level 3", "Level 4", "Level 5", "Level 6", "Level 7", "Level 8", "Level 9"}

After I swap 2nd and 4th positioned elements in the RecyclerView. The updated list should look like this (which I want):

{"Level 1", "Level 5", "Level 2", "Level 3", "Level 4", "Level 6", "Level 7", "Level 8", "Level 9"}

But I am getting this updated list:

{"Level 1", "Level 5", "Level 3", "Level 4", "Level 2", "Level 6", "Level 7", "Level 8", "Level 9"}
sagar suri
  • 4,351
  • 12
  • 59
  • 122

4 Answers4

6

I believe that your problem is that you use Collections.swap(itemList, fromPosition, toPosition);, which swaps objects on the specified positions. So the output is correct. What you need, though:

String item = itemList.remove(fromPostion);
itemList.insert(item, toPosition);
MeliX
  • 230
  • 1
  • 6
  • I want to check the updated list. where can i check the updated list in the adapter after drag and drop? I cant check the list inside `onItemMove()`. I wanna compare the updated list with another list. – sagar suri Jul 20 '17 at 09:22
  • @Hiren, do you want to check if the final combination is right? Well, I would suggest to do so when all `RecyclerView`'s animations have finished. I think it can be done by using custom `ItemAnimator` suggested in this thread https://stackoverflow.com/questions/33710605/detect-animation-finish-in-androids-recyclerview – MeliX Jul 20 '17 at 09:34
2

Code here working for me.

 @Override
public boolean onItemMove(int fromPosition, int toPosition) {
    if (fromPosition < toPosition) {
        for (int i = fromPosition; i < toPosition; i++) {
            Collections.swap(listData, i, i + 1);
        }
    } else {
        for (int i = fromPosition; i > toPosition; i--) {
            Collections.swap(listData, i, i - 1);
        }
    }
    notifyItemMoved(fromPosition, toPosition);
    return true;
}
Do Xuan Nguyen
  • 189
  • 4
  • 8
1

Try calling notifyDataSetChanged() in onItemMove menthod.

@Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        notifyItemMoved(fromPosition, toPosition);
        notifyItemChanged(fromPosition);
        notifyItemChanged(toPosition);
        notifyDataSetChanged();    
        return true;
    }
Zeeshan Sardar
  • 235
  • 3
  • 15
0

Try this instead. In your case, you will first have to find the index of the item to be replaced (fromPosition) and then add toPosition to that index. How to move specific item in array list to the first item

Bruke
  • 71
  • 6