Di recente mi sono imbattuto in questo grande Gist che offre un'implementazione funzionante di una sorta di trascinamento ListView, senza necessità di dipendenze esterne.
Fondamentalmente si tratta di creare il tuo adattatore personalizzato che si estende ArrayAdaptercome classe interna all'attività che contiene il tuo ListView. Su questo adattatore uno quindi imposta un onTouchListenerai tuoi elementi di elenco che segnalerà l'inizio del trascinamento.
In quel Gist hanno impostato l'ascoltatore su una parte specifica del layout dell'elemento della lista (la "maniglia" dell'elemento), in modo da non spostarlo accidentalmente premendo qualsiasi parte di esso. Personalmente, ho preferito andare con un onLongClickListenerinvece, ma spetta a te decidere. Ecco un estratto di quella 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;
}
}
Ciò comporta anche l'aggiunta di un carattere onTouchListenera ListView, che verifica se un elemento viene trascinato, gestisce lo scambio e l'invalidazione e interrompe lo stato di trascinamento. Un estratto di quella 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;
}
});
Infine, ecco come appaiono i metodi stopDrage startDrag, che gestiscono l'abilitazione e la disabilitazione del processo di trascinamento:
public void startDrag(String string) {
mPosition = -1;
mSortable = true;
mDragString = string;
mAdapter.notifyDataSetChanged();
}
public void stopDrag() {
mPosition = -1;
mSortable = false;
mDragString = null;
mAdapter.notifyDataSetChanged();
}