Arrastrar y soltar en un `ListView`
Estoy tratando de implementar un arrastrar y soltar en un ListView
en android (Ice Cream Sandwich). Así que cuando el objeto arrastrado alcanza el borde del ListView
, estoy desplazando el ListView
en la dirección pertinente. El problema es que cuando nos desplácese, a veces el adaptador crea nuevas View
como necesarias y estas 'nuevas' View
no recibieron el evento ACTION_DRAG_STARTED
anteriormente y por lo tanto no reciben las actualizaciones de DragEvent
. ¿Hay alguna manera de enviar los eventos a estas vistas también?
- Comunicación P2P NFC entre Galaxy S3 y ACR122
- La vinculación de datos de Android pasa los argumentos al método onClick
- ListView - getView se llama demasiadas veces
- Obtener el ancho y la altura de una fila en RecycleView - Android
- ¿Cómo actualizar la tabla en sqlite?
- Android: "Expresión esperada" al pasar una clase de actividad
- Cómo encender la linterna y la cámara frontal al mismo tiempo en android
- Escribir enum con String a paquete
- No se puede resolver nada.
- ¿Cuándo reciclar mapa de bits en el proyecto android?
- Fondos de Android Live con OpenGL ES 2.0?
- Android RadioGroup / RadioButton NullPointerException
- Java Jackson org.codehaus.jackson.map.exc.UnrecognizedPropertyException
Una forma más fácil de implementar arrastrar y soltar en listview es utilizar esta gran biblioteca. https://github.com/commonsguy/cwac-touchlist vale la pena intentarlo.
Mirando la fuente para ver, veo:
static final int DRAG_CAN_ACCEPT = 0x00000001; int mPrivateFlags2; boolean canAcceptDrag() { return (mPrivateFlags2 & DRAG_CAN_ACCEPT) != 0; }
MPrivateFlags2 es paquete privado y no expuesto por el SDK. Sin embargo, debería ser capaz de cambiarlo en una subclase haciendo:
try { Field mPrivateFlags2 = this.getClass().getField("mPrivateFlags2"); int currentValue = mPrivateFlags2.getInt(this); mPrivateFlags2.setInt(this, currentValue | 0x00000001); } catch (Exception e) { }
Tengo el mismo problema . No solucioné este problema de reciclaje, pero encontré una posible solución aún utilizando el marco Drag & Drop. La idea es cambiar de perspectiva: en lugar de usar un OnDragListener
en cada View
de la lista, se puede utilizar directamente en el ListView
.
Entonces la idea es encontrar en la parte superior del elemento que el dedo es mientras se realiza el Drag & Drop, y para escribir el código de visualización relacionados en el ListAdapter
de la ListView
. El truco es entonces encontrar en la parte superior de la vista del artículo que somos, y donde se hace la caída.
Para ello, establezco como id
en cada vista creada por el adaptador su posición ListView
– con View.setId()
, para poder encontrarla más tarde usando una combinación de ListView.pointToPosition()
y ListView.findViewById()
.
Como un ejemplo de escucha de arrastre (que es, te lo recuerdo, aplicado en el ListView
), puede ser algo así:
// Initalize your ListView private ListView _myListView = new ListView(getContext()); // Start drag when long click on a ListView item _myListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view); view.startDrag(null, shadowBuilder, _myListView.getItemAtPosition(position), 0); return true; } }); // Set the adapter and drag listener _myListView.setOnDragListener(new MyListViewDragListener()); _myListView.setAdapter(new MyViewAdapter(getActivity())); // Classes used above private class MyViewAdapter extends ArrayAdapter<Object> { public MyViewAdapter (Context context, List<TimedElement> objects) { super(context, 0, objects); } @Override public View getView(int position, View convertView, ViewGroup parent) { View myView = convertView; if (myView == null) { // Instanciate your view } // Associates view and position in ListAdapter, needed for drag and drop myView.setId(position); return myView; } } private class MyListViewDragListener implements View.OnDragListener { @Override public boolean onDrag(View v, DragEvent event) { final int action = event.getAction(); switch(action) { case DragEvent.ACTION_DRAG_STARTED: return true; case DragEvent.ACTION_DRAG_DROP: // We drag the item on top of the one which is at itemPosition int itemPosition = _myListView.pointToPosition((int)event.getX(), (int)event.getY()); // We can even get the view at itemPosition thanks to get/setid View itemView = _myListView.findViewById(itemPosition ); /* If you try the same thing in ACTION_DRAG_LOCATION, itemView * is sometimes null; if you need this view, just return if null. * As the same event is then fired later, only process the event * when itemView is not null. * It can be more problematic in ACTION_DRAG_DROP but for now * I never had itemView null in this event. */ // Handle the drop as you like return true; } } }
Ahora si usted necesita tener una regeneración visual al hacer un arrastrar y soltar, hay varias estrategias. Por ejemplo, puede tener 2 variables de instancia en su actividad denominada:
private boolean ongoingDrag = false; // To know if we are in a drag&drop state private int dragPosition = 0; // You put the itemPosition variable here
Al hacer la MyListViewDragListener
arrastrar y soltar en MyListViewDragListener
, modifica estas variables y usa su estado en MyViewAdapter
. Por supuesto no olvide actualizar la interfaz de usuario (en el hilo de eventos, por supuesto, utilice un Handler) con algo como.
El problema es porque listView.getPositionForView (view) devuelve -1 si la vista no está visible cuando se llama. Así que confiar en que se producirá un error cuando se desplaza la lista. Por lo tanto, en lugar de establecer un view.setOnLongClickListener () puede establecer un listView.setOnItemLongClickListener () en el elemento de lista que llama a startDrag () en el elemento. OnItemLongClick () le da la posición que puede pasar a en el parámetro myLocalState de startDrag (). Entonces usted recupera eso en onDrag () usando event.getLocalState () y el molde de él a un entero. Me gusta esto…
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { position -= listView.getHeaderViewsCount(); DragShadowBuilder dragShadow = new View.DragShadowBuilder(view); view.startDrag(null, dragShadow, position, 0); return true; } });
Entonces en su OnDragListener …
@Override public boolean onDrag(View eventView, DragEvent event) { Integer dragViewPos = ((Integer) event.getLocalState()); int eventViewPos = listView.getPositionForView(eventView) - listView.getHeaderViewsCount(); ... }
- Guardar la interfaz de usuario en el cambio de orientación – onSaveInstanceState no funciona como se esperaba si se conserva el fragmento
- Agrupar por en ContentResolver en Ice Cream Sandwich