Drag & Drop in RecyclerView mit GridLayoutManager

Was ich erreichen möchte: Habe einen RecyclerView mit GridLayoutManager, der Drag'n'Drop unterstützt und das beim Ziehen die Elemente umgibt.

Nebennote: Erstmaliges Waschen mit Drag & Drop.

Es gibt viele Themen, wie man diese Funktion mit einem ListView erreichen kann, zum Beispiel: https://raw.githubusercontent.com/btownrippleman/FurthestProgress/master/FurthestProgress/src/com/anappforthat/android/languagelineup/DynamicListView.java

Allerdings sind die Beispiele in der Regel eine Menge Code mit, Erstellen von Bitmaps der gezogenen Ansicht und es fühlt sich an, wie es möglich sein sollte, das gleiche Ergebnis mit View.startDrag(...) und RecyclerView mit notifyItemAdded() , notifyItemMoved() und notifyItemRemoved() da sie notifyItemRemoved() anordnen.

Also habe ich gegen einige gespielt und kam mit:

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

Dieses Stück Code Art der Arbeit, ich bekomme das Swapping, aber sehr instabil / zittrig und manchmal, wenn es das ganze Raster erfrischt, wird wieder in die ursprüngliche Bestellung oder auf etwas zufälliges umgeordnet. Wie auch immer der Code oben ist nur mein erster schneller Versuch, was ich wirklich mehr daran interessiert bin zu wissen, ist, wenn es irgendeine Standard- / Best Practice-Möglichkeit gibt, den Drag & Drop mit ReyclerView zu machen oder wenn der richtige Weg, es zu lösen, immer noch dasselbe ist Seit Jahren für ListViews verwendet?

  • Recyclerview schmerzhaft langsam, um zwischengespeicherte Bilder zu laden, bilden Picasso
  • Wie Auto scroll up Recycler-Ansicht in Android?
  • Ausgabe mit Selektor auf RecycleView
  • Es kann kein Algorithmus zum Ausfiltern von Elementen in einem RecyclerView geschrieben werden, basierend auf einer langen Speicherung mit jedem Element
  • Android -RecyclerView in Kitkat
  • Android RecyclerView Fehler
  • Android setOnScrollListner auf RecyclerView veraltet
  • Alternative zu registerDataSetObserver () in RecyclerView
  • 3 Solutions collect form web for “Drag & Drop in RecyclerView mit GridLayoutManager”

    Es gibt tatsächlich einen besseren Weg, dies zu erreichen. Sie können einige der RecyclerView "Companion" Klassen verwenden:

    ItemTouchHelper , das ist

    Eine Utility-Klasse zum Hinzufügen von Swipe zu entlassen und Drag & Drop Unterstützung für RecyclerView.

    Und seine ItemTouchHelper.Callback , die ist

    Den Vertrag zwischen ItemTouchHelper und Ihrem Antrag

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

    Weitere Details finden Sie in der Dokumentation.

    Dies ist meine Lösung mit Datenbank-Neuordnung:

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

    Es gibt eine Unterstützung Funktionen tahat nutzen 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(); } 

    Ich fand Advancedrecyclerview sehr nützlich. Guck mal!!!

    Das Android ist ein Google Android Fan-Website, Alles ├╝ber Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.