Android List View Drag & Drop-Sortierung

132

Ich habe eine Liste von Datensätzen in einer Listenansicht, die der Benutzer per Drag & Drop neu sortieren kann. Ich habe dies in anderen Apps implementiert gesehen, aber ich habe kein Tutorial dafür gefunden. Es muss etwas sein, das auch andere brauchen. Kann mich jemand auf einen Code dafür verweisen?

miannelle
quelle
1
Ich habe dieses Tutorial gefunden, das beim Erstellen einer sortierbaren Listenansicht hilfreich sein kann . Ich habe es noch nicht getestet, aber das Video sieht vielversprechend aus
Alex
@Arkde kein Scherz, sie haben noch Jahre später keine Antwort auf diese Frage akzeptiert.
ArtOfWarfare
@ArtOfWarfare Ich denke, man sollte bedenken, dass dem Fragesteller etwas Unglückliches passiert sein könnte ... weitere Aktivitäten nicht zuzulassen.
Heycosmo
1
@heycosmo Es ist möglich ... laut ihrem SO-Profil hat miannelle zuletzt nur einen Monat nach dem Stellen der Frage besucht. Tolle Arbeit an der DSLV ... Ich habe einige Änderungen daran vorgenommen, damit Dinge wie doppeltes Tippen zum Duplizieren von Elementen und zum Ändern des Schattens des Elements beim Ziehen verwendet werden können (meine Elemente haben jeweils ihre Zeilennummer, also ich Ich habe es so gemacht, dass die Aktualisierungen der Zeilennummern beim Ziehen aktualisiert werden können.) Sie sind nur gehackt, weit weniger elegant als alles andere in der Klasse, weshalb ich die Änderungen nicht an GitHub übermittelt habe.
ArtOfWarfare
github.com/bauerca/drag-sort-listview Ich verwende dies und ist es möglich, eine REIHE auf LongClick of Listview zu ziehen?
Achin

Antworten:

85

Daran arbeite ich schon seit einiger Zeit. Es ist schwierig, es richtig zu machen, und ich behaupte nicht, dass ich es tue, aber ich bin bisher damit zufrieden. Mein Code und mehrere Demos finden Sie unter

Seine Verwendung ist dem TouchInterceptor (auf dem der Code basiert) sehr ähnlich, obwohl wesentliche Änderungen an der Implementierung vorgenommen wurden.

DragSortListView bietet einen reibungslosen und vorhersehbaren Bildlauf beim Ziehen und Mischen von Elementen. Das Mischen von Elementen stimmt viel besser mit der Position des ziehenden / schwebenden Elements überein. Listenelemente mit heterogener Höhe werden unterstützt. Das Drag-Scrolling ist anpassbar (ich demonstriere das schnelle Drag-Scrollen durch eine lange Liste - nicht, dass mir eine Anwendung in den Sinn kommt). Kopf- / Fußzeilen werden respektiert. etc.?? Schau mal.

Heycosmo
quelle
3
Ihre Lösung funktioniert wie ein Zauber. Viel besser als andere. Was ist die Lizenz Ihres Quellcodes? Apache 2.0?
Dariusz Bacinski
1
@heycosmo Ich habe einige Probleme beim Erstellen eines Layouts in meiner App mit den in der Demo bereitgestellten Ansichten. Fehler bei mehreren Namespaces. Könnten Sie bitte einen kleinen Blogpost über die Verwendung des von Ihnen bereitgestellten Codes verfassen?
daniel_c05
Gibt es eine Möglichkeit, Code so zu ändern, dass Elemente nicht nur beim Vorbeischauen sortiert werden, während Sie Ihr Element darüber ziehen?
Alex Semeniuk
@heycosmo Können Sie Ihr GitHub-Repository zugänglich machen? Es scheint offline zu sein ...
sdasdadas
8
Das bauerca repo wird nicht mehr gepflegt. Diese Gabel ist aktiver: github.com/JayH5/drag-sort-listview
ecdpalma
21

Jetzt ist es RecyclerViewmit ItemTouchHelper ziemlich einfach zu implementieren . Überschreiben Sie einfach die onMoveMethode von ItemTouchHelper.Callback:

 @Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    mMovieAdapter.swap(viewHolder.getAdapterPosition(), target.getAdapterPosition());
    return true;
}

Ein ziemlich gutes Tutorial dazu finden Sie auf medium.com: Drag & Swipe mit RecyclerView

vogelig
quelle
18

Füge diese Antwort zum Zweck derer hinzu, die darüber googeln.

Vor kurzem gab es eine Episode von DevBytes ( ListView Cell Dragging and Rearranging ), in der erklärt wird, wie das geht

Sie finden es hier auch den Beispielcode finden Sie hier .

Was dieser Code im Grunde tut, ist, dass er eine dynamic listviewdurch die Erweiterung von erstellt listview, die das Ziehen und Austauschen von Zellen unterstützt. Damit Sie das DynamicListViewanstelle Ihres Basic verwenden können ListView und das ist es, haben Sie eine ListView mit Drag & Drop implementiert.

Arun C.
quelle
1
Beachten Sie bei der Verwendung der DevBytes-Implementierung, dass Ihr Adapter und DynamicListView-Brei dieselbe Instanz verwenden, wenn Objekte ein Array bilden. Andernfalls funktioniert die Implementierung nicht. Dies ist kein Problem für statische Listen, kann aber eine Herausforderung für Loaders sein
AAverin
Ich habe das Beispiel auf einer aktuellen 5.0 Android-Version ausprobiert. Es hat jetzt einige Probleme ...
Torsten B
@TCA Ja, ich habe auch Probleme mit 5.0. Bitte helfen Sie.
Manu
6

Die DragListView-Bibliothek macht dies wirklich ordentlich mit sehr guter Unterstützung für benutzerdefinierte Animationen wie Höhenanimationen. Es wird auch weiterhin regelmäßig gewartet und aktualisiert.

So verwenden Sie es:

1: Fügen Sie die Bibliothek hinzu, um zuerst zu gradeln

dependencies {
    compile 'com.github.woxthebox:draglistview:1.2.1'
}

2: Liste aus XML hinzufügen

<com.woxthebox.draglistview.DragListView
    android:id="@+id/draglistview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

3: Stellen Sie den Drag Listener ein

mDragListView.setDragListListener(new DragListView.DragListListener() {
    @Override
    public void onItemDragStarted(int position) {
    }

    @Override
    public void onItemDragEnded(int fromPosition, int toPosition) {
    }
});

4: Erstellen Sie einen Adapter, der von DragItemAdapter überschrieben wird

public class ItemAdapter extends DragItemAdapter<Pair<Long, String>, ItemAdapter.ViewHolder>
    public ItemAdapter(ArrayList<Pair<Long, String>> list, int layoutId, int grabHandleId, boolean dragOnLongPress) {
        super(dragOnLongPress);
        mLayoutId = layoutId;
        mGrabHandleId = grabHandleId;
        setHasStableIds(true);
        setItemList(list);
}

5: Implementieren Sie einen Ansichtshalter, der sich von DragItemAdapter.ViewHolder aus erstreckt

public class ViewHolder extends DragItemAdapter.ViewHolder {
    public TextView mText;

    public ViewHolder(final View itemView) {
        super(itemView, mGrabHandleId);
        mText = (TextView) itemView.findViewById(R.id.text);
    }

    @Override
    public void onItemClicked(View view) {
    }

    @Override
    public boolean onItemLongClicked(View view) {
        return true;
    }
}

Weitere Informationen finden Sie unter https://github.com/woxblom/DragListView

Wox
quelle
5

Ich fand, dass DragSortListView gut funktioniert hat, obwohl der Einstieg einfacher hätte sein können. Hier ist ein kurzes Tutorial zur Verwendung in Android Studio mit einer In-Memory-Liste:

  1. Fügen Sie dies zu den build.gradleAbhängigkeiten für Ihre App hinzu:

    compile 'asia.ivity.android:drag-sort-listview:1.0' // Corresponds to release 0.6.1
  2. Erstellen Sie eine Ressource für die Drag-Handle-ID, indem Sie Folgendes erstellen oder hinzufügen values/ids.xml:

    <resources>
        ... possibly other resources ...
        <item type="id" name="drag_handle" />
    </resources>
  3. Erstellen Sie ein Layout für ein Listenelement, das Ihr bevorzugtes Drag-Handle-Bild enthält, und weisen Sie seine ID der ID zu, die Sie in Schritt 2 erstellt haben (z drag_handle. B. ).

  4. Erstellen Sie ein DragSortListView-Layout, ungefähr so:

    <com.mobeta.android.dslv.DragSortListView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:dslv="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        dslv:drag_handle_id="@id/drag_handle"
        dslv:float_background_color="@android:color/background_light"/>
  5. Legen Sie eine ArrayAdapterAbleitung mit einer getViewÜberschreibung fest, die Ihre Listenelementansicht darstellt.

        final ArrayAdapter<MyItem> itemAdapter = new ArrayAdapter<MyItem>(this, R.layout.my_item, R.id.my_item_name, items) { // The third parameter works around ugly Android legacy. http://stackoverflow.com/a/18529511/145173
            @Override public View getView(int position, View convertView, ViewGroup parent) {
                View view = super.getView(position, convertView, parent);
                MyItem item = getItem(position);
                ((TextView) view.findViewById(R.id.my_item_name)).setText(item.getName());
                // ... Fill in other views ...
                return view;
            }
        };
    
        dragSortListView.setAdapter(itemAdapter);
  6. Legen Sie einen Drop-Listener fest, der die Elemente beim Ablegen neu anordnet.

        dragSortListView.setDropListener(new DragSortListView.DropListener() {
            @Override public void drop(int from, int to) {
                MyItem movedItem = items.get(from);
                items.remove(from);
                if (from > to) --from;
                items.add(to, movedItem);
                itemAdapter.notifyDataSetChanged();
            }
        });
Edward Brey
quelle
3

Ich bin kürzlich auf diesen großartigen Kern gestoßen , der eine funktionierende Implementierung einer Drag-Sortierung ListViewbietet, ohne dass externe Abhängigkeiten erforderlich sind.


Grundsätzlich besteht es darin, einen benutzerdefinierten Adapter zu erstellen, der sich ArrayAdapterals innere Klasse auf die Aktivität erstreckt, die Ihren enthält ListView. Auf diesem Adapter setzt man dann einen onTouchListenerauf Ihre Listenelemente, der den Beginn des Ziehens signalisiert.

In diesem Gist setzen sie den Listener auf einen bestimmten Teil des Layouts des Listenelements (das "Handle" des Elements), sodass man es nicht versehentlich bewegt, indem man einen Teil davon drückt. Persönlich habe ich es vorgezogen, stattdessen mit einem zu gehen onLongClickListener, aber es liegt an Ihnen, zu entscheiden. Hier ein Auszug aus diesem Teil:

public class MyArrayAdapter extends ArrayAdapter<String> {

    private ArrayList<String> mStrings = new ArrayList<String>();
    private LayoutInflater mInflater;
    private int mLayout;

    //constructor, clear, remove, add, insert...

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        View view = convertView;
        //inflate, etc...

        final String string = mStrings.get(position);
        holder.title.setText(string);

        // Here the listener is set specifically to the handle of the layout
        holder.handle.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    startDrag(string);
                    return true;
                }
                return false;
            }
        });

        // change color on dragging item and other things...         

        return view;
    }
}

Dazu gehört auch das Hinzufügen eines onTouchListenerzu ListView, das prüft, ob ein Element gezogen wird, das Austauschen und die Ungültigmachung behandelt und den Ziehstatus stoppt. Ein Auszug aus diesem Teil:

mListView.setOnTouchListener(new View.OnTouchListener() {
     @Override
     public boolean onTouch(View view, MotionEvent event) {
        if (!mSortable) { return false; }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                // get positions
                int position = mListView.pointToPosition((int) event.getX(), 
                    (int) event.getY());
                if (position < 0) {
                    break;
                }
                // check if it's time to swap
                if (position != mPosition) {
                    mPosition = position;
                    mAdapter.remove(mDragString);
                    mAdapter.insert(mDragString, mPosition);
                }
                return true;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_OUTSIDE: {
                //stop drag state
                stopDrag();
                return true;
            }
        }
        return false;
    }
});

Abschließend sehen Sie, wie die Methoden stopDragund startDragaussehen, die das Aktivieren und Deaktivieren des Ziehvorgangs behandeln:

public void startDrag(String string) {
    mPosition = -1;
    mSortable = true;
    mDragString = string;
    mAdapter.notifyDataSetChanged();
}

public void stopDrag() {
    mPosition = -1;
    mSortable = false;
    mDragString = null;
    mAdapter.notifyDataSetChanged();
}
DarkCygnus
quelle
Dies scheint zwar eine der einfachsten Implementierungen zu sein, sieht aber schlecht aus (Zeilen wechseln nur, es gibt tatsächlich keine Looks), fühlt sich schlecht an und macht das Scrollen durch eine Liste, die nicht auf den Bildschirm passt, nahezu unmöglich (ich wechsle ständig die Zeilen statt Scrollen).
Heinzlmaen