Vue de liste Android Tri par glisser-déposer

132

J'ai une liste d'enregistrements dans une vue de liste que je veux que l'utilisateur puisse re-trier à l'aide d'une méthode de glisser-déposer. J'ai vu cela implémenté dans d'autres applications, mais je n'ai pas trouvé de tutoriel pour cela. Cela doit aussi être quelque chose dont les autres ont besoin. Quelqu'un peut-il me diriger vers un code pour faire cela?

miannelle
la source
1
J'ai trouvé ce tutoriel qui pourrait aider à créer une vue de liste triable . Je ne l'ai pas encore testé mais la vidéo semble prometteuse
Alex
@Arkde sans blague, ils n'ont toujours pas accepté de réponse à cette question, des années plus tard.
ArtOfWarfare
@ArtOfWarfare Je suppose qu'il faut considérer que quelque chose de malheureux aurait pu arriver au demandeur ... interdisant toute activité supplémentaire.
heycosmo
1
@heycosmo C'est possible ... selon leur profil SO, miannelle s'est rendue pour la dernière fois un mois après avoir posé la question. Aussi, excellent travail sur le DSLV ... J'ai apporté quelques modifications à celui-ci pour permettre des choses comme le double tap pour dupliquer des éléments et changer l'ombre de l'élément lorsqu'il est traîné (mes éléments ont chacun leur numéro de ligne dessus, donc je fait en sorte que les mises à jour des numéros de ligne puissent être mises à jour au fur et à mesure qu'elles sont glissées.) Elles sont simplement piratées, beaucoup moins élégantes que tout le reste de la classe, c'est pourquoi je n'ai pas soumis les modifications à GitHub.
ArtOfWarfare
github.com/bauerca/drag-sort-listview J'utilise ceci et est-il possible de faire glisser une ROW surLongClick de Listview?
Achin

Réponses:

85

J'y travaille depuis un certain temps maintenant. Difficile de bien faire, et je ne prétends pas le faire, mais j'en suis content jusqu'à présent. Mon code et plusieurs démos se trouvent sur

Son utilisation est très similaire au TouchInterceptor (sur lequel le code est basé), bien que d' importantes modifications d'implémentation aient été apportées.

DragSortListView a un défilement fluide et prévisible tout en faisant glisser et mélanger des éléments. Les mélanges d'éléments sont beaucoup plus cohérents avec la position de l'élément glissant / flottant. Les éléments de liste de hauteur hétérogène sont pris en charge. Le défilement par glisser-déposer est personnalisable (je démontre le défilement rapide par glisser-déposer dans une longue liste - pas qu'une application me vienne à l'esprit). Les en-têtes / pieds de page sont respectés. etc.?? Regarde.

heycosmo
la source
3
Votre solution fonctionne comme un charme. Beaucoup mieux que les autres. Qu'est-ce que la licence de votre code source? Apache 2.0?
Dariusz Bacinski
1
@heycosmo J'ai quelques problèmes lors de la création d'une mise en page dans mon application en utilisant les vues fournies dans la démo. Erreur de plusieurs espaces de noms, pourriez-vous s'il vous plaît peut-être faire un petit article de blog sur la façon d'utiliser le code que vous fournissez?
daniel_c05
Existe-t-il une possibilité de modifier le code de manière à ce que les éléments ne soient pas triés uniquement lors du dépôt pendant que vous faites glisser votre élément au-dessus d'eux?
Alex Semeniuk
@heycosmo Pouvez-vous rendre votre référentiel GitHub accessible? Il semble être hors ligne ...
sdasdadas
8
Le repo bauerca n'est plus maintenu. Cette fourchette est plus active: github.com/JayH5/drag-sort-listview
ecdpalma
21

Maintenant, c'est assez facile à mettre en œuvre RecyclerViewavec ItemTouchHelper . Remplacez simplement la onMoveméthode de ItemTouchHelper.Callback:

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

Un très bon tutoriel à ce sujet peut être trouvé sur medium.com: Glisser-faire glisser avec RecyclerView

birdy
la source
18

J'ajoute cette réponse aux fins de ceux qui recherchent sur Google à ce sujet.

Il y a eu un épisode de DevBytes ( ListView Cell Dragging and Rearranging ) récemment qui explique comment faire cela

Vous pouvez le trouver ici aussi l'exemple de code est disponible ici .

Ce que fait ce code, c'est qu'il crée un dynamic listviewpar l'extension de listviewqui prend en charge le glissement et l'échange de cellules. Pour que vous puissiez utiliser le à la DynamicListViewplace de votre base ListView et c'est tout, vous avez implémenté un ListView avec glisser-déposer.

Arun C
la source
1
Lorsque vous utilisez l'implémentation DevBytes, gardez à l'esprit que votre adaptateur et DynamicListView partagent la même instance si les objets array. Sinon, la mise en œuvre ne fonctionnera pas. Ce n'est pas un problème pour les listes statiques, mais peut être un défi avec les chargeurs
AAverin
J'ai essayé l'exemple sur une version Android 5.0 actuelle. Il a quelques problèmes maintenant ...
Torsten B
@TCA Ouais, j'ai aussi eu des problèmes sur 5.0. Veuillez aider.
Manu
6

La bibliothèque DragListView fait cela très bien avec un très bon support pour les animations personnalisées telles que les animations d'élévation. Il est également toujours entretenu et mis à jour régulièrement.

Voici comment vous l'utilisez:

1: Ajoutez d'abord la bibliothèque pour graduer

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

2: Ajouter une liste à partir de xml

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

3: Définissez l'écouteur glisser

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

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

4: créer un adaptateur remplacé à partir de DragItemAdapter

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: implémenter un visualiseur qui s'étend de DragItemAdapter.ViewHolder

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

Pour plus d'informations, rendez-vous sur https://github.com/woxblom/DragListView

Wox
la source
5

J'ai trouvé que DragSortListView fonctionnait bien, bien que commencer cela aurait pu être plus facile. Voici un bref tutoriel sur son utilisation dans Android Studio avec une liste en mémoire:

  1. Ajoutez ceci aux build.gradledépendances de votre application:

    compile 'asia.ivity.android:drag-sort-listview:1.0' // Corresponds to release 0.6.1
  2. Créez une ressource pour l'ID de poignée de glissement en créant ou en ajoutant à values/ids.xml:

    <resources>
        ... possibly other resources ...
        <item type="id" name="drag_handle" />
    </resources>
  3. Créez une mise en page pour un élément de liste qui inclut votre image de poignée de glissement préférée et attribuez son ID à l'ID que vous avez créé à l'étape 2 (par exemple drag_handle).

  4. Créez une disposition DragSortListView, quelque chose comme ceci:

    <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. Définissez un ArrayAdapterdérivé avec un getViewremplacement qui rend votre vue d'élément de liste.

        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. Définissez un écouteur de dépôt qui réorganise les éléments lorsqu'ils sont déposés.

        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
la source
3

Je suis récemment tombé sur ce grand Gist qui donne une implémentation fonctionnelle d'une sorte de drag ListView, sans dépendances externes nécessaires.


Fondamentalement, il consiste à créer votre adaptateur personnalisé s'étendant en ArrayAdaptertant que classe interne à l'activité contenant votre ListView. Sur cet adaptateur, on définit ensuite un onTouchListenersur vos éléments de liste qui signalera le début du glissement.

Dans ce Gist, ils définissent l'auditeur sur une partie spécifique de la mise en page de l'élément de liste (la "poignée" de l'élément), de sorte que l'on ne le déplace pas accidentellement en appuyant sur une partie de celui-ci. Personnellement, j'ai préféré aller avec un à la onLongClickListenerplace, mais c'est à vous de décider. Voici un extrait de cette partie:

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

Cela implique également l'ajout d'un onTouchListenerà ListView, qui vérifie si un élément est déplacé, gère l'échange et l'invalidation et arrête l'état de glissement. Un extrait de cette partie:

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

Enfin, voici à quoi ressemblent les méthodes stopDraget startDrag, qui gèrent l'activation et la désactivation du processus de glissement:

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
la source
Bien que cela semble être l'une des implémentations les plus simples, cela semble mauvais (les lignes changent simplement, il n'y a en fait aucun look), cela se sent mal et rend le défilement d'une liste qui ne correspond pas à l'écran presque impossible (je change constamment de ligne au lieu de défilement).
Heinzlmaen