Vista de lista de Android Arrastrar y soltar ordenar

132

Tengo una lista de registros en una vista de lista que deseo que el usuario pueda volver a ordenar usando un método de arrastrar y soltar. He visto esto implementado en otras aplicaciones, pero no he encontrado un tutorial para ello. Debe ser algo que otros también necesitan. ¿Alguien puede señalarme algún código para hacer esto?

miannelle
fuente
1
Encontré este tutorial que podría ayudar a crear una vista de lista ordenable . Todavía no lo he probado, pero el video parece prometedor
Alex
@Arkde no es broma, todavía no han aceptado una respuesta para esta pregunta, años después.
ArtOfWarfare
@ArtOfWarfare Supongo que uno debería considerar que algo desafortunado podría haberle sucedido al autor de la pregunta ... no permitir más actividades.
heycosmo
1
@heycosmo Es posible ... según su perfil SO, miannelle visitó por última vez solo un mes después de hacer la pregunta. Además, un gran trabajo en el DSLV ... Hice algunas modificaciones para permitir que cosas como tocar dos veces duplicaran elementos y cambiar la sombra del elemento a medida que se arrastra (cada uno de ellos tiene su número de fila, así que lo hizo para que las actualizaciones del número de fila puedan actualizarse a medida que se arrastra.) Son simplemente pirateadas, mucho menos elegantes que todo lo demás en la clase, por lo que no he enviado los cambios a GitHub.
ArtOfWarfare
github.com/bauerca/drag-sort-listview Estoy usando esto y ¿es posible arrastrar una FILA en LongClick de Listview?
Achin

Respuestas:

85

He estado trabajando en esto por algún tiempo ahora. Es difícil hacerlo bien, y no pretendo hacerlo, pero hasta ahora estoy contento con eso. Mi código y varias demostraciones se pueden encontrar en

Su uso es muy similar al TouchInterceptor (en el que se basa el código), aunque se han realizado cambios significativos en la implementación.

DragSortListView tiene un desplazamiento suave y predecible mientras arrastra y baraja elementos. Los elementos aleatorios son mucho más consistentes con la posición del elemento arrastrado / flotante. Se admiten elementos de lista de altura heterogénea. El desplazamiento de arrastre es personalizable (demuestro el desplazamiento rápido de arrastre a través de una larga lista, no es que se me ocurra una aplicación). Encabezados / pies de página son respetados. etc. ?? Echar un vistazo.

heycosmo
fuente
3
Su solución funciona a las mil maravillas. Mucho mejor que otros. ¿Qué es la licencia de su código fuente? Apache 2.0?
Dariusz Bacinski
1
@heycosmo Tengo algunos problemas al crear un diseño en mi aplicación usando las vistas proporcionadas en la demostración. Error en varios espacios de nombres, ¿podría hacer una pequeña publicación en el blog sobre cómo usar el código que proporciona?
daniel_c05
¿Existe la posibilidad de modificar el código de una manera que los elementos se ordenen no solo cuando los arrastra por encima?
Alex Semeniuk
@heycosmo ¿Puedes hacer que tu repositorio de GitHub sea accesible? Parece estar fuera de línea ...
sdasdadas
8
El repositorio bauerca ya no se mantiene. Esta bifurcación está más activa: github.com/JayH5/drag-sort-listview
ecdpalma
21

Ahora es bastante fácil de implementar RecyclerViewcon ItemTouchHelper . Simplemente anule el onMovemétodo de ItemTouchHelper.Callback:

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

Puede encontrar un tutorial bastante bueno sobre esto en medium.com: arrastre y deslice con RecyclerView

pajarito
fuente
18

Estoy agregando esta respuesta para los que buscan en Google sobre esto ...

Hubo un episodio de DevBytes ( ListView Cell Dragging and Rearranging) ) que explica cómo hacer esto

Puede encontrarlo aquí también, el código de muestra está disponible aquí .

Lo que básicamente hace este código es que crea una dynamic listviewextensión listviewque admite el arrastre y el intercambio de celdas. Para que pueda usar el en DynamicListViewlugar de su básico ListView y eso es todo, ha implementado un ListView con arrastrar y soltar.

Arun C
fuente
1
Cuando use la implementación de DevBytes, tenga en cuenta que su adaptador y DynamicListView mush comparten la misma instancia si los objetos se agrupan. De lo contrario, la implementación no funcionará. Esto no es un problema para las listas estáticas, pero puede ser un desafío con los cargadores
AAverin
Probé el ejemplo en una versión actual de Android 5.0. Tiene algunos problemas ahora ...
Torsten B
@TCA Sí, yo también tuve problemas con 5.0. Por favor ayuda.
Manu
6

DragListView lib hace esto muy bien con un soporte muy agradable para animaciones personalizadas, como animaciones de elevación. También se mantiene y actualiza regularmente.

Así es como lo usas:

1: agregue la lib a gradle primero

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

2: Agregar lista de xml

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

3: configurar el oyente de arrastre

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

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

4: Crear un adaptador anulado desde 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: Implemente un marcador de vista que se extienda desde 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;
    }
}

Para obtener información más detallada, vaya a https://github.com/woxblom/DragListView

Wox
fuente
5

Encontré que DragSortListView funcionaba bien, aunque comenzar a usarlo podría haber sido más fácil. Aquí hay un breve tutorial sobre cómo usarlo en Android Studio con una lista en memoria:

  1. Agregue esto a las build.gradledependencias para su aplicación:

    compile 'asia.ivity.android:drag-sort-listview:1.0' // Corresponds to release 0.6.1
  2. Cree un recurso para la ID del controlador de arrastre creando o agregando a values/ids.xml:

    <resources>
        ... possibly other resources ...
        <item type="id" name="drag_handle" />
    </resources>
  3. Cree un diseño para un elemento de la lista que incluya su imagen de control de arrastre favorita y asigne su ID a la ID que creó en el paso 2 (por ejemplo drag_handle).

  4. Cree un diseño DragSortListView, algo como esto:

    <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. Establezca una ArrayAdapterderivada con una getViewanulación que represente la vista de su elemento de lista.

        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. Establezca un oyente de caída que reorganice los elementos a medida que se caen.

        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
fuente
3

Recientemente me topé con este gran Gist que ofrece una implementación funcional de tipo de arrastre ListView, sin necesidad de dependencias externas.


Básicamente consiste en crear su Adaptador personalizado que se extienda ArrayAdaptercomo una clase interna a la actividad que contiene su ListView. En este adaptador, uno establece un elemento onTouchListeneren su Lista de elementos que indicará el inicio del arrastre.

En ese Gist establecen al oyente en una parte específica del diseño del elemento de la lista (el "identificador" del elemento), por lo que no se mueve accidentalmente presionando ninguna parte del mismo. Personalmente, preferí ir con un onLongClickListeneren su lugar, pero eso depende de ti decidir. Aquí un extracto de esa parte:

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

Esto también implica agregar un elemento onTouchListenera ListView, que comprueba si un elemento se está arrastrando, maneja el intercambio y la invalidación, y detiene el estado de arrastre. Un extracto de esa parte:

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

Finalmente, así es como se ven los métodos stopDragy startDrag, que manejan la habilitación y deshabilitación del proceso de arrastre:

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
fuente
Si bien esta parece ser una de las implementaciones más fáciles, se ve mal (las filas simplemente cambian, en realidad hay cero miradas), se siente mal y hace que sea imposible desplazarse por una lista que no se ajusta a la pantalla (constantemente cambio las filas en lugar de desplazamiento).
Heinzlmaen