Перетаскивание элементов в RecyclerView с помощью GridLayoutManager

То, что я хочу достичь: Получите RecyclerView с GridLayoutManager, который поддерживает drag'n'drop и переставляет элементы при перетаскивании.

Сторона примечания: Первое время развивающее что-либо с перетаскиванием.

Существует множество тем, как добиться этой функции с помощью ListView, например: https://raw.githubusercontent.com/btownrippleman/FurthestProgress/master/FurthestProgress/src/com/anappforthat/android/languagelineup/DynamicListView.java

Однако в примерах обычно много кода, создавая растровые изображения перетаскиваемого представления, и кажется, что можно добиться такого же результата, используя View.startDrag(...) и RecyclerView с notifyItemAdded() , notifyItemMoved() и notifyItemRemoved() поскольку они предоставляют переупорядоченные анимации.

Поэтому я играл вокруг некоторых и придумал это:

 final CardAdapter adapter = new CardAdapter(list); adapter.setHasStableIds(true); adapter.setListener(new CardAdapter.OnLongClickListener() { @Override public void onLongClick(View view) { ClipData data = ClipData.newPlainText("",""); View.DragShadowBuilder builder = new View.DragShadowBuilder(view); final int pos = mRecyclerView.getChildAdapterPosition(view); final Goal item = list.remove(pos); mRecyclerView.setOnDragListener(new View.OnDragListener() { int prevPos = pos; @Override public boolean onDrag(View view, DragEvent dragEvent) { final int action = dragEvent.getAction(); switch(action) { case DragEvent.ACTION_DRAG_LOCATION: View onTopOf = mRecyclerView.findChildViewUnder(dragEvent.getX(), dragEvent.getY()); int i = mRecyclerView.getChildAdapterPosition(onTopOf); list.add(i, list.remove(prevPos)); adapter.notifyItemMoved(prevPos, i); prevPos = i; break; case DragEvent.ACTION_DROP: View underView = mRecyclerView.findChildViewUnder(dragEvent.getX(), dragEvent.getY()); int underPos = mRecyclerView.getChildAdapterPosition(underView); list.add(underPos, item); adapter.notifyItemInserted(underPos); adapter.notifyDataSetChanged(); break; } return true; } }); view.startDrag(data, builder, view, 0); } }); mRecyclerView.setAdapter(adapter); 

Эта часть кода похожа на работу, я получаю обмен, но очень нестабилен / шаткий, а иногда, когда он обновляется, вся сетка перестраивается обратно в исходный порядок или что-то случайное. В любом случае, приведенный выше код – это только моя первая быстрая попытка, и я больше заинтересован в том, чтобы знать, есть ли какой-то стандарт / лучший способ сделать перетаскивание с помощью ReyclerView или если правильный способ его решения по-прежнему остается тем же, что и Были использованы для ListViews в течение многих лет?

Solutions Collecting From Web of "Перетаскивание элементов в RecyclerView с помощью GridLayoutManager"

На самом деле есть лучший способ добиться этого. Вы можете использовать некоторые классы «компаньона» RecyclerView :

ItemTouchHelper , который

Класс утилиты для добавления прокрутки для удаления и перетаскивания и возврата в RecyclerView.

И его ItemTouchHelper.Callback , который

Договор между ItemTouchHelper и вашей заявкой

 // Create an `ItemTouchHelper` and attach it to the `RecyclerView` ItemTouchHelper ith = new ItemTouchHelper(_ithCallback); ith.attachToRecyclerView(rv); // Extend the Callback class ItemTouchHelper.Callback _ithCallback = new ItemTouchHelper.Callback() { //and in your imlpementaion of public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { // get the viewHolder's and target's positions in your adapter data, swap them Collections.swap(/*RecyclerView.Adapter's data collection*/, viewHolder.getAdapterPosition(), target.getAdapterPosition()); // and notify the adapter that its dataset has changed _adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition()); return true; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { //TODO } //defines the enabled move directions in each state (idle, swiping, dragging). @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { return makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.START | ItemTouchHelper.END); } }; 

Для получения дополнительной информации проверьте их документацию.

Это мое решение с переупорядочением базы данных:

  ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { final int fromPosition = viewHolder.getAdapterPosition(); final int toPosition = target.getAdapterPosition(); if (fromPosition < toPosition) { for (int i = fromPosition; i < toPosition; i++) { Collections.swap(mAdapter.getCapitolos(), i, i + 1); } } else { for (int i = fromPosition; i > toPosition; i--) { Collections.swap(mAdapter.getCapitolos(), i, i - 1); } } mAdapter.notifyItemMoved(fromPosition, toPosition); return true; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { MyViewHolder svH = (MyViewHolder ) viewHolder; int index = mAdapter.getCapitolos().indexOf(svH.currentItem); mAdapter.getCapitolos().remove(svH.currentItem); mAdapter.notifyItemRemoved(index); if (emptyView != null) { if (mAdapter.getCapitolos().size() > 0) { emptyView.setVisibility(TextView.GONE); } else { emptyView.setVisibility(TextView.VISIBLE); } } } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); reorderData(); } }; ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback); itemTouchHelper.attachToRecyclerView(recList); 

Существуют функции поддержки tahat, использующие AsyncTask:

 private void reorderData() { AsyncTask<String, Void, Spanned> task = new AsyncTask<String, Void, Spanned>() { @Override protected Spanned doInBackground(String... strings) { dbService.deleteAllData(); for (int i = mAdapter.getCapitolos().size() - 1; i >= 0; i--) { Segnalibro s = mAdapter.getCapitolos().get(i); dbService.saveData(s.getIdCapitolo(), s.getVersetto()); } return null; } @Override protected void onPostExecute(Spanned spanned) { } }; task.execute(); } 

Я нашел Advancedrecyclerview весьма полезным. Взгляни!!!