6

I am trying to use a RecyclerView with a vertical LinearLayoutManager in order to display a list items. This list can contain several different item types (differents layouts), and it can be reordered by the user using drag and drop.

For the item types, as documented in Android documentation, I have overridden the getItemType method in order to handle different types of views in the recycler and handle it in the onCreateViewHolder and onBindViewHolder. This works like a charm.

For the drag and drop reorder, I have used a ItemTouchHelper.Callback (inspired by this sample project). This also works well.

The problem happen when I try to use different items types AND the drag and drop behaviour. As long as the drag occurs between items of the same type, this works well, but when i'm draggin a view of type A over a view of type B, the drag stop and the view returned to it's original position.

Here is my code:

MyFragment.java

public class MyFragment extends Fragment implements MyAdapter.Listener {

private MyViewModel mViewModel;
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
private ItemTouchHelper mItemTouchHelper;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment, container, false);
    mRecyclerView = root.findViewById(R.id.recyclerView);

    mAdapter = new MyAdapter(this);
    mRecyclerView.setAdapter(mAdapter);
    mItemTouchHelper = new ItemTouchHelper(new MyDragHelperCallback(mAdapter));
    mItemTouchHelper.attachToRecyclerView(mRecyclerView);


    mViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
    mViewModel.addObserver(this, new Observer<List<Item>>() {
        @Override
        public void onChanged(@Nullable List<Item> items) {
            mAdapter.updateList(items);
        }
    });
    mAdapter.updateList(mViewModel.getList());

    return root;
}

@Override
public void onStartDragRequest(@NonNull RecyclerView.ViewHolder viewHolder) {
    mItemTouchHelper.startDrag(viewHolder);
}
}

MyDragHelperCallback.java

public class MyDragHelperCallback extends ItemTouchHelper.Callback {

private static final int DRAG_MOVEMENT_FLAGS = ItemTouchHelper.UP | ItemTouchHelper.DOWN;

@NonNull
private MyDragListener mListener;

public MyDragHelperCallback(@NonNull MyDragListener listener) {
    mListener = listener;
}

@Override
public boolean isLongPressDragEnabled() {
    return true;
}

@Override
public boolean isItemViewSwipeEnabled() {
    return false;
}

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if (!(recyclerView.getLayoutManager() instanceof LinearLayoutManager)) {
        throw new IllegalArgumentException("Should only be used with a LinearLayoutManager");
    }

    return makeMovementFlags(DRAG_MOVEMENT_FLAGS, 0);
}

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

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    // Nothing to do
}

}

MyDragListener.java

public interface MyDragListener {
    void onItemMoved(int from, int to);
}

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyHolder> implements MyDragListener {

    @Nullable
    private List<Item> mList;

    public void updateList(@Nullable List<Item> list) {
        mList = list;
        notifyDataSetChanged();
    }

    @Override
    public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == 0) {
            return new MyHolderBis(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_bis, parent, false));
        }
        return new MyHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false));
    }

    @Override
    public void onBindViewHolder(final MyHolder holder, int position) {
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                mListener.onStartDragRequest(holder);
                return true;
            }
        });
        // bind views to data
    }

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

    @Override
    public int getItemViewType(int position) {
        return mList.get(position).getType();
    }

    @Override
    public void onItemMoved(int from, int to) {
        notifyItemMoved(from, to);
    }
}

Is there something I missing ? Or is this simply to possible to achieve ?

Thanks for all your answers !

user3112140
  • 133
  • 1
  • 6

0 Answers0