Aufgaben-App: Sortieren mit Drag & Drop

Wie wär’s, wenn man die Aufgaben in unserer Aufgaben-App sortieren könnte? Am Besten durch Drag&Drop, also anfassen, gedrückt halten und dann nach oben oder unten verschieben. Lass uns das machen!
Bisheriger Quellcode
Hier kannst du den Quellcode der Aufgaben-App herunterladen und sofort mitmachen. Das Sortieren per Drag&Drop ist noch nicht drin. Download der Aufgaben-App
Berührungen erwünscht
Um verschiedene Gesten (Wischen, Drag&Drop, etc.) auf Listen auszuführen, benötigen wir einen so genannten „ItemTouchHelper“, also eine Hilfsklasse für das Berühren der Zellen, sprich der Aufgaben in unserer Liste. Wir instanziieren diese Klasse in der initViews-Methode unserer MainActivity und verbinden sie mit der RecyclerView.

TaskListTouchCallback
Als nächstes benötigen wir eine so gennante Callback-Klasse, die nennen wir „TaskListTouchCallback“. Dort implementieren wir den Code, der ausgeführt werden soll, wenn der Nutzer eine Aufgabe über unter unter eine andere Aufgabe verschiebt, also sortiert.
Wir erstellen eine neue Java-Klasse im Package-Ordner unserer App, wie wir das schon öfter gemacht haben. Wichtig ist, dass die neue Klasse von einer bereits vorhandenen Klasse erbt, nämlich von der ItemTouchHelper.SimpleCallback.
class TaskListTouchCallback extends ItemTouchHelper.SimpleCallback {
Die Klassensignatur wird rot unterstrichen. Das liegt daran, dass wir unbedingt zwei Methoden implementieren müssen, die in der SimpleCallback-Klasse abstrakt definiert sind. Abstrakte Klassen und Methoden sind sozusagen vom Entwickler für einen bestimmten Einsatz vorbereitet, benötigen jedoch noch etwas Code, der ganz individuell für jede App anders ist.
Am einfachsten ist es, wenn du mit der Maus auf die unterstrichene Zeile klickst, so dass links eine rote Glühbirne erscheint. Klick die Glühbirne an, sie gibt dir Tipps, wie man Fehler behebt. Wähle den ersten Menüpunkt, der dort erscheint. Das sollte „Implement methods“ sein.

Es erscheint ein Dialog, der die die beiden Methoden anzeigt, die wir implementieren müssen. Das sind onMove und onSwiped. Bestätige mit OK. Die beiden Methoden werden erzeugt. Im Moment sind sie noch leer, darum kümmern wir uns gleich.

Die Klassensignatur ist immer noch rot unterstrichen. Also klicken wir sie wieder an, klicken wieder auf die rote Glühbirne und schauen was das Problem ist. Offenbar fehlt der Konstruktor, der ist hier zwingend erforderlich. Klicke auf den Lösungsvorschlag der Glühbirne, damit der Konstruktor automatisch erzeugt wird.

Das sieht jetzt wild aus. Keine Sorge, das ist nur der Kommentar zum Konstruktor. Alles was grau und rot ist, kannst du löschen. Lies es dir vorher mal durch, wenn du möchtest. Im wesentlichen wird dort beschrieben, dass man zwei Argumente übergeben kann. Das eine Argument heißt „dragDirs“, das andere „swipeDirs“. Mit diesen Argumenten kann man die Richtung angeben, in die Drag&Drop bzw. Swipen (wischen) erlaubt sein sollen.
Zum Sortieren wollen wir beide Richtungen zulassen, also nach oben und nach unten. Dazu übergeben wir zwei Konstanten, die wir mit einem senkrechten Strich verbinden. Dieser Strich nennt sich „Pipe“ und steht für „und“, also oben und unten. Den Strich findest du links unten auf deiner Windows-Tastatur.
Wischen machen wir später. Das zweite Argument setzen wir also einfach auf 0. Die Übergabe dieser Argumente schreiben wir in den super-Aufruf. Außerdem löschen wir die beiden Argumente aus der Signatur des Konstruktors. Wir brauchen das ja nicht von außen übergeben. Wir legen es einfach fest.

TaskListTouchCallback.onMove
Damit unsere App weiß, was passieren soll, wenn der Nutzer eine Aufgabe über eine andere verschiebt, müssen wir die onMove-Methode implementieren. Uns werden dort drei Argumente übergeben: Die RecyclerView und zwei ViewHolder. Der erste ViewHolder hat die Position der Aufgabe, die verschoben wird, also die Ursprungsposition. Und der zweite ViewHolder hat die Position, an die die Aufgabe verschoben wurde, also die Zielposition. Diese beiden Postionen sollten wir aus den ViewHoldern herausholen und lokal in Variablen speichern.
int oldPosition = viewHolder.getAdapterPosition(); int newPosition = target.getAdapterPosition();
Um eine Aufgabe von einer zur anderen Position in unserer Liste zu verschieben, sollten wir den TaskManager nutzen. Der TaskManager hat alle Aufgaben und ist zuständig für deren Verwaltung. Also lass uns den TaskManager öffnen und dort eine Methode hinzufügen.
Diese Methode kommt in den TaskManager:

Die Methode nennen wir moveTask. Wir übergeben die Position, an der sich die Aufgabe befindet, und die Position, an die sie verschoben werden soll. Dann kommt die Logik. Wir suchen uns die Aufgabe anhand der Ursprungsposition aus der lokalen Aufgabenliste, entfernen sie aus der Liste und fügen sie erneut hinzu, jedoch an einer anderen Position, nämlich an der Zielposition. Und nicht vergessen: Die Liste speichern!
Nun können wir diese neue Methode in onMove vom TaskListTouchCallback nutzen. Die onMove-Methode sieht also so aus:

Der TaskManager wird rot angezeigt. Das liegt daran, dass er in unserer TaskListTouchCallback-Klasse unbekannt ist, also nicht existiert. Am Besten übergeben wir ihn im Konstruktor und speichern ihn als Attribut unserer Klasse:

TaskListTouchCallback.onSwiped
Was ist mit Wischen? In die onSwiped-Methode kommt der Code, der für das Wischen von Aufgaben zuständig ist. Das machen wir beim nächsten Mal. Schaffst du es auch alleine? Versuch’s mal! 😉
MainActivity vervollständigen
Unsere TaskListTouchCallback-Klasse müssen wir als Argument an den ItemTouchHelper übergeben. Achte darauf, dabei auch den TaskManager an die neue Instanz der TaskListTouchCallback-Klasse zu übergeben. Der Aufruf sieht also so aus:
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new TaskListTouchCallback(taskManager));
itemTouchHelper.attachToRecyclerView(recyclerView);
Das war’s schon! Viel Spaß beim draggen und droppen! 🙂