Ziehen und wischen Sie in RecyclerView. Teil 2: Drag & Drop-Controller, Raster und benutzerdefinierte Animationen

Ziehen und wischen Sie in RecyclerView. Teil 2: Drag & Drop-Controller, Raster und benutzerdefinierte Animationen


Im ersten Teil haben wir uns mit ItemTouchHelper und der Implementierung von ItemTouchHelper.Callback befasst , mit dem RecyclerView die grundlegenden Drag & Drop- und Swipe-to- Discharge -Funktionen RecyclerView . In diesem Artikel werden wir die vorherigen Schritte fortsetzen, indem wir Unterstützung für das Anordnen von Elementen in Form eines Rasters, Drag & Drop-Controllern, die Auswahl eines Listenelements und benutzerdefinierte Wischanimationen hinzufügen.


Netz von Elementen


Drag & Drop-Controller


Beim Erstellen einer Liste, die Drag & Drop unterstützt , wird normalerweise die Möglichkeit implementiert, Elemente per Touch per Drag & Drop zu verschieben . Dies trägt zur Klarheit und Benutzerfreundlichkeit der Liste im "Bearbeitungsmodus" bei und wird auch von Materialrichtlinien empfohlen. Das Hinzufügen von Drag & Drop-Controllern zu unserem Beispiel ist unglaublich einfach.


Drag & Drop-Controller


Aktualisieren Sie zunächst das layout Elements ( item_main.xml ).


 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item" android:layout_width="match_parent" android:layout_height="?listPreferredItemHeight" android:clickable="true" android:focusable="true" android:foreground="?selectableItemBackground"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="16dp" android:textAppearance="?android:attr/textAppearanceMedium" /> <ImageView android:id="@+id/handle" android:layout_width="?listPreferredItemHeight" android:layout_height="match_parent" android:layout_gravity="center_vertical|right" android:scaleType="center" android:src="@drawable/ic_reorder_grey_500_24dp" /> </FrameLayout> 

Das für den Drag Controller verwendete Bild befindet sich in den Material Design-Symbolen und wird mithilfe des praktischen Symbolgenerator-Plugins in Android Studio zum Projekt hinzugefügt.


Wie in einem vorherigen Artikel kurz erwähnt, können Sie mit ItemTouchHelper.startDrag(ViewHolder) programmgesteuert ein Drag & Drop starten. Wir müssen also nur den ViewHolder aktualisieren, indem wir einen Drag-Controller hinzufügen und einen einfachen Touch-Handler einrichten, der startDrag() .


Wir benötigen eine Schnittstelle, um Ereignisse entlang der Kette zu übertragen:


 public interface OnStartDragListener { /** * Called when a view is requesting a start of a drag. * * @param viewHolder The holder of the view to drag. */ void onStartDrag(RecyclerView.ViewHolder viewHolder); } 

Definieren Sie dann im ImageView eine ImageView für den Drag Controller:


 public final ImageView handleView; public ItemViewHolder(View itemView) { super(itemView); // ... handleView = (ImageView) itemView.findViewById(R.id.handle); } 

und aktualisieren Sie den RecyclerListAdapter :


 private final OnStartDragListener mDragStartListener; public RecyclerListAdapter(OnStartDragListener dragStartListener) { mDragStartListener = dragStartListener; // ... } @Override public void onBindViewHolder(final ItemViewHolder holder, int position) { // ... holder.handleView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { mDragStartListener.onStartDrag(holder); } return false; } }); } 

RecyclerListAdapter sollte jetzt RecyclerListAdapter so aussehen.


Sie müssen OnStartDragListener den OnStartDragListener zum Fragment hinzufügen:


 public class RecyclerListFragment extends Fragment implements OnStartDragListener { // ... @Override public void onViewCreated(View view, Bundle icicle) { super.onViewCreated(view, icicle); RecyclerListAdapter a = new RecyclerListAdapter(this); // ... } @Override public void onStartDrag(RecyclerView.ViewHolder viewHolder) { mItemTouchHelper.startDrag(viewHolder); } } 

RecyclerListFragment sollte jetzt so aussehen. Wenn Sie jetzt die Anwendung starten, können Sie mit dem Ziehen und Ablegen beginnen, indem Sie den Controller berühren.


Listenelemente ziehen


Markieren eines Listenelements


In unserem Beispiel gibt es keine visuelle Anzeige für das zu ziehende Objekt. Natürlich sollte dies nicht sein, aber es ist leicht zu beheben. Mit ItemTouchHelper Sie Standard-Effekte zur Hervorhebung von ItemTouchHelper verwenden. Bei Lollipop und späteren Versionen von Android „verteilt“ sich die Hintergrundbeleuchtung während der Interaktion mit dem Element. In früheren Versionen ändert das Element einfach seine Farbe in abgedunkelt.


Um dies in unserem Beispiel zu implementieren, fügen Sie einfach einen Hintergrund ( background Eigenschaft) zum Stamm- FrameLayout Elements FrameLayout oder legen Sie ihn im Konstruktor RecyclerListAdapter.ItemViewHolder fest . Es wird ungefähr so ​​aussehen:


Elementauswahl


Es sieht cool aus, aber vielleicht möchten Sie noch mehr kontrollieren. Eine Möglichkeit, dies zu tun, besteht darin, ViewHolder erlauben, ViewHolder eines Elements zu verarbeiten. Zu diesem ItemTouchHelper.Callback bietet ItemTouchHelper.Callback zwei weitere Methoden:


  • onSelectedChanged(ViewHolder, int) wird jedes Mal aufgerufen, wenn sich der Status eines Elements in Drag ( ACTION_STATE_DRAG ) oder Swipe ( ACTION_STATE_SWIPE ) ändert . Dies ist ein idealer Ort, um den Status der view in aktiv zu ändern.
  • clearView(RecyclerView, ViewHolder) wird aufgerufen, wenn die Drag & Drop- view abgeschlossen ist und auch wenn das Wischen abgeschlossen ist ( ACTION_STATE_IDLE ). Hier wird normalerweise der Ausgangszustand Ihrer view wiederhergestellt.

Lassen Sie uns jetzt einfach alles zusammenfügen.


Erstellen Sie zunächst eine Schnittstelle, die ViewHolders implementiert:


 /** * Notifies a View Holder of relevant callbacks from * {@link ItemTouchHelper.Callback}. */ public interface ItemTouchHelperViewHolder { /** * Called when the {@link ItemTouchHelper} first registers an * item as being moved or swiped. * Implementations should update the item view to indicate * it's active state. */ void onItemSelected(); /** * Called when the {@link ItemTouchHelper} has completed the * move or swipe, and the active item state should be cleared. */ void onItemClear(); } 

Implementieren Sie dann im SimpleItemTouchHelperCallback die entsprechenden Methoden:


 @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { // We only want the active item if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { if (viewHolder instanceof ItemTouchHelperViewHolder) { ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemSelected(); } } super.onSelectedChanged(viewHolder, actionState); } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); if (viewHolder instanceof ItemTouchHelperViewHolder) { ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemClear(); } } 

Jetzt muss nur noch RecyclerListAdapter.ItemViewHolder ItemTouchHelperViewHolder implementieren:


 public class ItemViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder { // ... @Override public void onItemSelected() { itemView.setBackgroundColor(Color.LTGRAY); } @Override public void onItemClear() { itemView.setBackgroundColor(0); } } 

In diesem Beispiel fügen wir einfach einen grauen Hintergrund hinzu, während das Element aktiv ist, und entfernen ihn dann. Wenn Ihr ItemTouchHelper und Ihr Adapter eng miteinander verbunden sind, können Sie diese Einstellung einfach aufgeben und den Status der view direkt in ItemTouchHelper.Callback .


Netze


Wenn Sie jetzt versuchen, den GridLayoutManager , werden Sie GridLayoutManager , dass er nicht richtig funktioniert. Der Grund und die Lösung sind einfach: Wir müssen unserem ItemTouchHelper dass wir das Ziehen von Elementen nach links und rechts unterstützen möchten. Zuvor in SimpleItemTouchHelperCallback wir bereits SimpleItemTouchHelperCallback angegeben:


 @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; return makeMovementFlags(dragFlags, swipeFlags); } 

Die einzige Änderung, die zur Unterstützung der Gitter erforderlich ist, besteht darin, die entsprechenden Flags hinzuzufügen:


 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; 

Swipe-to-Dism ist jedoch kein sehr natürliches Verhalten für swipeFlags , sodass swipeFlags höchstwahrscheinlich swipeFlags :


 @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; int swipeFlags = 0; return makeMovementFlags(dragFlags, swipeFlags); } 

GridLayoutManager Beispiel für ein funktionierendes GridLayoutManager finden Sie unter RecyclerGridFragment . So sieht es beim Start aus:


Gitterelemente


Benutzerdefinierte Swipe-Animationen


ItemTouchHelper.Callback bietet eine sehr bequeme Möglichkeit, die Animation beim Ziehen oder Fegen vollständig zu steuern. Da ItemTouchHelper ein RecyclerView.ItemDecoration ist , können wir auf ähnliche Weise in das Rendern einer view eingreifen. Im nächsten Teil werden wir diese Frage genauer untersuchen. Schauen wir uns zunächst ein einfaches Beispiel für das Überschreiben der Standard-Wischanimation an, um ein lineares Verschwinden anzuzeigen.


 @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { float width = (float) viewHolder.itemView.getWidth(); float alpha = 1.0f - Math.abs(dX) / width; viewHolder.itemView.setAlpha(alpha); viewHolder.itemView.setTranslationX(dX); } else { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } } 

Die dY dX und dY sind die aktuelle Verschiebung relativ zur ausgewählten view , wobei:


  • -1.0f ist ein vollständiger Wisch von rechts nach links (von ItemTouchHelper.END zu ItemTouchHelper.START )
  • 1.0f wird vollständig von links nach rechts ItemTouchHelper.START (von ItemTouchHelper.START zu ItemTouchHelper.END )

Es ist wichtig, super für jeden actionState , den Sie nicht verarbeiten, um die Standardanimation auszulösen.


Im nächsten Teil betrachten wir ein Beispiel, in dem wir das Zeichnen eines Elements im Moment des Drag & Drop steuern.


Fazit


Tatsächlich macht das Einrichten von ItemTouchHelper ziemlich viel Spaß. Um das Volumen dieses Artikels nicht zu erhöhen, habe ich ihn in mehrere unterteilt.


Quellcode


Den vollständigen Code für diese Artikelserie finden Sie in den GitHub-Repositorys von Android-ItemTouchHelper-Demo . Dieser Artikel behandelt Commits von ef8f149 bis d164fba .


← Ziehen und Wischen in RecyclerView. Teil 1: ItemTouchHelper

Source: https://habr.com/ru/post/de428419/


All Articles