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 ArrayAdapter
como una clase interna a la actividad que contiene su ListView
. En este adaptador, uno establece un elemento onTouchListener
en 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 onLongClickListener
en 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 onTouchListener
a 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 stopDrag
y 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();
}