Widok listy systemu Android Sortowanie metodą przeciągnij i upuść

132

Mam listę rekordów w widoku listy, którą chcę, aby użytkownik mógł ponownie posortować za pomocą metody przeciągnij i upuść. Widziałem to zaimplementowane w innych aplikacjach, ale nie znalazłem dla niego samouczka. To musi być coś, czego inni też potrzebują. Czy ktoś może wskazać mi jakiś kod do zrobienia tego?

miannelle
źródło
1
Znalazłem ten samouczek, który może pomóc w tworzeniu sortowanego widoku listy . Jeszcze tego nie testowałem, ale wideo wygląda obiecująco
Alex
@Arkde bez żartów, po latach wciąż nie zaakceptowali odpowiedzi na to pytanie.
ArtOfWarfare
@ArtOfWarfare Myślę, że należy wziąć pod uwagę, że coś niefortunnego mogło przytrafić się pytającemu ... uniemożliwiając dalsze działania.
heycosmo
1
@heycosmo Jest to możliwe ... zgodnie z ich profilem SO, miannelle ostatnio odwiedzali nas zaledwie miesiąc po zadaniu pytania. Świetna robota na DSLV ... Dokonałem kilku modyfikacji, aby umożliwić takie rzeczy, jak podwójne stuknięcie w celu zduplikowania elementów i zmianę cienia elementu podczas przeciągania (każdy z moich elementów ma swój numer wiersza, więc sprawiło, że aktualizacje numerów wierszy mogą być aktualizowane podczas przeciągania.) Są po prostu włamane, znacznie mniej eleganckie niż wszystko inne w klasie, dlatego nie przesłałem zmian do GitHub.
ArtOfWarfare
github.com/bauerca/drag-sort-listview Używam tego i czy jest możliwe przeciągnięcie WIERSZA na LongClick of Listview?
Achin,

Odpowiedzi:

85

Pracuję nad tym już od jakiegoś czasu. Trudno jest uzyskać rację i nie twierdzę, że tak, ale jak na razie jestem z tego zadowolony. Mój kod i kilka wersji demonstracyjnych można znaleźć pod adresem

Jego użycie jest bardzo podobne do TouchInterceptora (na którym oparty jest kod), chociaż wprowadzono znaczące zmiany implementacyjne.

DragSortListView zapewnia płynne i przewidywalne przewijanie podczas przeciągania i tasowania elementów. Tasowanie przedmiotów jest znacznie bardziej spójne z pozycją przeciąganego / unoszącego się przedmiotu. Obsługiwane są elementy listy o niejednorodnej wysokości. Przewijanie metodą przeciągania można dostosować (pokazuję szybkie przewijanie przeciąganiem po długiej liście - nie chodzi o aplikację). Nagłówki / stopki są przestrzegane. itp.?? Spójrz.

heycosmo
źródło
3
Twoje rozwiązanie działa jak urok. Znacznie lepiej niż inni. Jaka jest licencja twojego kodu źródłowego? Apache 2.0?
Dariusz Bacinski
1
@heycosmo Mam kilka problemów podczas tworzenia układu w mojej aplikacji przy użyciu widoków przedstawionych w wersji demonstracyjnej. Kilka błędów przestrzeni nazw, czy mógłbyś napisać mały post na blogu o tym, jak używać dostarczonego kodu?
daniel_c05
Czy istnieje możliwość zmodyfikowania kodu w taki sposób, aby elementy były sortowane nie tylko po upuszczeniu, podczas gdy przeciągasz swój przedmiot nad nimi?
Alex Semeniuk
@heycosmo Czy możesz udostępnić swoje repozytorium GitHub? Wygląda na to, że jest offline ...
sdasdadas
8
Repozytorium bauerca nie jest już utrzymywane. Ten widelec jest bardziej aktywny: github.com/JayH5/drag-sort-listview
ecdpalma
21

Teraz jest to dość łatwe do wykonania dla RecyclerViewz ItemTouchHelper . Po prostu zastąp onMovemetodę z ItemTouchHelper.Callback:

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

Całkiem dobry poradnik na ten temat można znaleźć na medium.com: Przeciągnij i przesuń z RecyclerView

birdy
źródło
18

Dodaję tę odpowiedź w celu szukania informacji w Google.

Niedawno pojawił się odcinek DevBytes ( ListView Cell Dragging and Rebranging ), który wyjaśnia, jak to zrobić

Możesz go znaleźć tutaj, również przykładowy kod jest dostępny tutaj .

To, co zasadniczo robi ten kod, polega na tym, że tworzy dynamic listviewrozszerzenie, listviewktóre obsługuje przeciąganie i zamianę komórek. Abyś mógł użyć DynamicListViewzamiast swojego podstawowego ListView i to wszystko, zaimplementowałeś ListView za pomocą przeciągania i upuszczania.

Arun C
źródło
1
Korzystając z implementacji DevBytes, należy pamiętać, że adapter i DynamicListView mp współużytkują tę samą instancję, jeśli object array. W przeciwnym razie implementacja nie zadziała. Nie stanowi to problemu w przypadku list statycznych, ale może stanowić wyzwanie dla
programów ładujących
Wypróbowałem przykład na aktualnej wersji Androida 5.0. Ma teraz pewne problemy ...
Torsten B
@TCA Tak, ja też mam problemy z wersją 5.0. Proszę pomóż.
Manu
6

Biblioteka DragListView robi to naprawdę schludnie z bardzo dobrą obsługą niestandardowych animacji, takich jak animacje wysokości. Jest również nadal utrzymywany i regularnie aktualizowany.

Oto jak go używasz:

1: Najpierw dodaj bibliotekę do gradle

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

2: Dodaj listę z xml

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

3: Ustaw odbiornik przeciągania

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

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

4: Utwórz kartę przesłoniętą z 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: Zaimplementuj element widoku, który rozszerza się z 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;
    }
}

Aby uzyskać bardziej szczegółowe informacje, przejdź do https://github.com/woxblom/DragListView

Wox
źródło
5

Zauważyłem, że DragSortListView działa dobrze, chociaż rozpoczęcie pracy mogło być łatwiejsze. Oto krótki samouczek dotyczący używania go w Android Studio z listą w pamięci:

  1. Dodaj to do build.gradlezależności dla swojej aplikacji:

    compile 'asia.ivity.android:drag-sort-listview:1.0' // Corresponds to release 0.6.1
    
  2. Utwórz zasób dla identyfikatora uchwytu przeciągania, tworząc lub dodając do values/ids.xml:

    <resources>
        ... possibly other resources ...
        <item type="id" name="drag_handle" />
    </resources>
    
  3. Utwórz układ elementu listy, który zawiera twój ulubiony obraz uchwytu przeciągania i przypisz jego identyfikator do identyfikatora utworzonego w kroku 2 (np drag_handle.).

  4. Utwórz układ DragSortListView, coś takiego:

    <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. Ustaw ArrayAdapterpochodną z getViewnadpisaniem, które renderuje widok elementu listy.

        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. Ustaw odbiornik upuszczania, który przestawia elementy w miarę ich upuszczania.

        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
źródło
4

Niedawno natknąłem się na ten wspaniały Gist, który daje działającą implementację typu przeciągania ListView, bez konieczności stosowania zewnętrznych zależności.


Zasadniczo polega na utworzeniu własnego adaptera rozszerzającego się ArrayAdapterjako klasa wewnętrzna do działania zawierającego ListView. Na tym adapterze ustawia się następnie onTouchListenerna elementy listy, które będą sygnalizować początek przeciągania.

W tym streszczeniu ustawiają słuchacza na określoną część układu elementu listy („uchwyt” elementu), więc nie można przypadkowo przesunąć go przez naciśnięcie jakiejkolwiek jego części. Osobiście wolałem onLongClickListenerzamiast tego, ale decyzja należy do ciebie. Oto fragment tej części:

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

Obejmuje to również dodanie onTouchListenerdo ListView, które sprawdza, czy element jest przeciągany, obsługuje zamianę i unieważnianie oraz zatrzymuje stan przeciągania. Fragment tej części:

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

Wreszcie, oto jak wyglądają metody stopDragi startDrag, które obsługują włączanie i wyłączanie procesu przeciągania:

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
źródło
Chociaż wydaje się to być jedną z najłatwiejszych implementacji, wygląda źle (wiersze po prostu się przełączają, w rzeczywistości jest zero), czuje się źle i sprawia, że ​​przewijanie listy, która nie pasuje do ekranu, jest prawie niemożliwe (ciągle zmieniam wiersze zamiast przewijanie).
Heinzlmaen