26

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

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

Есть много тем о том, как достичь этой функции с помощью 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 в течение многих лет?

ответ

76

Существует на самом деле лучший способ достичь этого. Вы можете использовать некоторые из „компаньонов“ классов RecyclerView «s:

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); 
    } 
}; 

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

+0

Интересно, исследуем это решение в понедельник, но оно кажется многообещающим. –

+0

@PaulBurke: Я обновил код, но оригинальная идея состояла в том, чтобы дать обзор метода и оставить подробности в документации – stan0

+0

@ stan0 Up-voted. Надеемся, что OP примет ответ сейчас (что он завершен). –

7

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

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(); 
} 
+0

Если возможно, поделитесь своим кодом кода 'mAdapter.getCapitolos()' в вашем адаптере. – Shailesh

+0

это просто ArrayList с данными, которые были прочитаны из базы данных в начале. –

+0

Спасибо за советы. – Shailesh

Смежные вопросы