viewholder studio soltar ejemplo drop arrastrar and java android drag-and-drop android-listview scroll

java - studio - Arrastrar y soltar en un `ListView`



drag layout android (4)

Estoy intentando implementar un arrastrar y soltar en un ListView en Android (Ice Cream Sandwich). Entonces, cuando el objeto arrastrado alcanza el borde del ListView , estoy desplazando el ListView en la dirección correspondiente. El problema es que cuando nos desplazamos, a veces el adaptador crea nuevas View según sea necesario 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 que pueda enviar los eventos a estas vistas también?


El problema se debe a que listView.getPositionForView (vista) devuelve -1 si la vista no está visible cuando se llama. Así que confiar en eso fallará cuando desplazas la lista. Entonces, en lugar de configurar un view.setOnLongClickListener (), puede establecer un listView.setOnItemLongClickListener () en el elemento de la lista que llama a startDrag () en el elemento. onItemLongClick () te da la posición a la que puedes pasar en el parámetro myLocalState de startDrag (). Luego lo recuperas en onDrag () usando event.getLocalState () y lo conviertes en 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; } });

Luego en tu OnDragListener ...

@Override public boolean onDrag(View eventView, DragEvent event) { Integer dragViewPos = ((Integer) event.getLocalState()); int eventViewPos = listView.getPositionForView(eventView) - listView.getHeaderViewsCount(); ... }


En cuanto a la fuente de vista, veo:

static final int DRAG_CAN_ACCEPT = 0x00000001; int mPrivateFlags2; boolean canAcceptDrag() { return (mPrivateFlags2 & DRAG_CAN_ACCEPT) != 0; }

mPrivateFlags2 es un paquete privado y no está expuesto por el SDK. Sin embargo, debería poder 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 resolví este problema de reciclaje, pero encontré una posible solución al usar el marco de arrastrar y soltar. La idea es cambiar de perspectiva: en lugar de usar un OnDragListener en cada View de la lista, se puede usar directamente en ListView .

Luego, la idea es encontrar encima de qué elemento está el dedo mientras se hace arrastrar y soltar, y escribir el código de visualización relacionado en el ListAdapter de ListAdapter de ListView . El truco consiste entonces en encontrar encima de qué vista de elemento estamos y dónde se realiza la eliminación.

Para hacer eso, establezco una id para cada vista creada por el adaptador en su posición de ListView - con View.setId() , por lo que puedo encontrarlo más adelante utilizando una combinación de ListView.pointToPosition() y ListView.findViewById() .

Como ejemplo de escucha de arrastre (que es, te recuerdo, aplicado en 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 necesita tener una retroalimentación visual al hacer un arrastrar y soltar, hay varias estrategias. Por ejemplo, puedes tener 2 variables de instancia en tu actividad llamada:

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 realizar la MyListViewDragListener arrastrar y soltar en MyListViewDragListener , modifica estas variables y utiliza su estado en MyViewAdapter . Por supuesto, no olvide actualizar la interfaz de usuario (en el subproceso del evento, por supuesto, use un controlador) con algo como el método _myListView.invalidate() .