Classificação de arrastar e soltar na exibição de lista do Android


132

Eu tenho uma lista de registros em uma exibição de lista que desejo que o usuário possa classificar novamente usando um método de arrastar e soltar. Eu já vi isso implementado em outros aplicativos, mas não encontrei um tutorial para isso. Deve ser algo que os outros também precisam. Alguém pode me indicar algum código para fazer isso?


1
Encontrei este tutorial que pode ajudar a criar uma exibição de lista classificável . Eu não testei ainda, mas os olhares de vídeo prometendo
Alex

@Arkde sem brincadeira, eles ainda não aceitaram uma resposta para esta pergunta, anos depois.
ArtOfWarfare 10/10/12

@ArtOfWarfare Acho que se deve considerar que algo lamentável poderia ter acontecido com o autor da pergunta ... não permitindo mais atividades.
11132 heycosmo

1
@heycosmo É possível ... de acordo com o perfil de SO, miannelle visitou pela última vez apenas um mês depois de fazer a pergunta. Além disso, um ótimo trabalho no DSLV ... Eu fiz algumas modificações para permitir que coisas como toque duplo duplicem itens e alterem a sombra do item à medida que ele é arrastado (meus itens têm o número da linha neles, então eu fizeram isso para que as atualizações do número da linha possam ser atualizadas à medida que são arrastadas.) Elas meio que são invadidas, muito menos elegantes do que tudo na classe, por isso não submeti as alterações ao GitHub.
ArtOfWarfare 12/11/12

github.com/bauerca/drag-sort-listview estou usando isso e é possível arrastar uma ROW noLongClick of Listview?
Achin

Respostas:


85

Estou trabalhando nisso há algum tempo. Difícil de acertar, e não afirmo que sim, mas estou feliz com isso até agora. Meu código e várias demos podem ser encontrados em

Seu uso é muito semelhante ao TouchInterceptor (no qual o código se baseia), embora tenham sido feitas alterações significativas na implementação.

O DragSortListView possui rolagem suave e previsível ao arrastar e embaralhar itens. As aleatórias de itens são muito mais consistentes com a posição do item de arrastar / flutuar. Os itens da lista de altura heterogênea são suportados. A rolagem por arraste é personalizável (eu demonstro uma rolagem rápida por uma longa lista - não que um aplicativo venha à mente). Cabeçalhos / rodapés são respeitados. etc. ?? Dê uma olhada.


3
Sua solução funciona como um encanto. Muito melhor que outros. Qual é a licença do seu código fonte? Apache 2.0?
Dariusz Bacinski

1
@heycosmo Tenho alguns problemas ao criar um layout no meu aplicativo usando as visualizações fornecidas na demonstração. Erro em vários namespaces, você poderia fazer um pequeno post no blog sobre como usar o código fornecido?
Daniel_c05

Existe a possibilidade de modificar o código de maneira que os itens sejam classificados não apenas quando você arrasta o item acima deles?
precisa

@heycosmo Você pode tornar seu repositório GitHub acessível? Parece estar offline ...
sdasdadas

8
O repo bauerca não é mais atualizado. Este fork é mais ativo: github.com/JayH5/drag-sort-listview
ecdpalma

21

Agora é muito fácil de implementar RecyclerViewcom o ItemTouchHelper . Basta substituir o 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;
}

Um tutorial muito bom sobre isso pode ser encontrado em medium.com: Arrastar e deslizar com o RecyclerView


18

Estou adicionando esta resposta com a finalidade de quem pesquisar sobre isso ..

Houve recentemente um episódio de DevBytes ( arrastar e reorganizar uma célula ListView ) que explica como fazer isso

Você pode encontrá-lo aqui também, o código de exemplo está disponível aqui .

O que esse código basicamente faz é que ele cria um dynamic listviewpela extensão listviewque suporta o arrastamento e troca de células. Para que você possa usar o em DynamicListViewvez do básico ListView e é isso, você implementou um ListView com Arrastar e Soltar.


1
Ao usar a implementação do DevBytes, lembre-se de que seu adaptador e o DynamicListView compartilham a mesma instância se os objetos forem arrays. Caso contrário, a implementação não funcionará. Este não é um problema para as listas estáticas, mas pode ser um desafio com Carregadeira
AAverin

Eu tentei o exemplo em uma versão atual do Android 5.0. Agora tem alguns problemas ...
Torsten B

@ TCA Sim, eu também tenho problemas no 5.0. Por favor ajude.
Manu

6

A biblioteca do DragListView faz isso muito legal, com um suporte muito bom para animações personalizadas, como animações de elevação. Também é mantido e atualizado regularmente.

Aqui está como você o usa:

1: Adicione a lib para classificar primeiro

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

2: Adicionar lista do xml

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

3: Defina o ouvinte de arrastar

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

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

4: Crie um adaptador substituído no 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: Implementar um viewholder que se estenda 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;
    }
}

Para informações mais detalhadas, acesse https://github.com/woxblom/DragListView


5

Descobri que o DragSortListView funcionava bem, embora a introdução dele pudesse ter sido mais fácil. Aqui está um breve tutorial sobre como usá-lo no Android Studio com uma lista na memória:

  1. Adicione isso às build.gradledependências do seu aplicativo:

    compile 'asia.ivity.android:drag-sort-listview:1.0' // Corresponds to release 0.6.1
  2. Crie um recurso para o ID da alça de arrasto criando ou adicionando a values/ids.xml:

    <resources>
        ... possibly other resources ...
        <item type="id" name="drag_handle" />
    </resources>
  3. Crie um layout para um item da lista que inclua sua imagem favorita da alça de arrasto e atribua seu ID ao ID que você criou na etapa 2 (por exemplo drag_handle).

  4. Crie um layout DragSortListView, algo como isto:

    <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. Defina uma ArrayAdapterderivada com uma getViewsubstituição que renderize a exibição do item 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. Defina um listener de recebimento que reorganize os itens conforme eles são descartados.

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

3

Recentemente, deparei-me com este excelente Gist que fornece uma implementação funcional de uma classificação por arraste ListView, sem dependências externas necessárias.


Basicamente, ele consiste em criar seu Adaptador customizado estendendo-se ArrayAdaptercomo uma classe interna para a atividade que contém o seu ListView. Nesse adaptador, um define um onTouchListenerpara seus Itens da lista que sinalizará o início do arrasto.

Nesse Gist, eles definem o ouvinte para uma parte específica do layout do Item da lista (o "identificador" do item), para que não seja movido acidentalmente pressionando qualquer parte dele. Pessoalmente, preferi optar por um onLongClickListener, mas isso depende de você decidir. Aqui um trecho dessa 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;
    }
}

Isso também envolve adicionar um onTouchListenerao ListView, que verifica se um item está sendo arrastado, manipula a troca e a invalidação e interrompe o estado do arrasto. Um trecho dessa 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;
    }
});

Por fim, eis a aparência dos métodos stopDrage startDrag, que tratam da ativação e desativação do processo de arrastar:

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

public void stopDrag() {
    mPosition = -1;
    mSortable = false;
    mDragString = null;
    mAdapter.notifyDataSetChanged();
}

Embora essa pareça ser uma das implementações mais fáceis, ela parece ruim (as linhas simplesmente mudam, na verdade não há aparência), parece ruim e torna impossível rolar uma lista que não se encaixa na tela quase impossível (eu constantemente troco de linha em vez de rolagem).
Heinzlmaen 5/12/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.