studio soltar objetos mover example drop arrastrar and android drag-and-drop

objetos - Arrastrar y soltar Android ACTION_DRAG_ENDED no se dispara



drag and drop list android example (2)

Realmente estoy teniendo un tiempo resolviendo esto y no puedo encontrar amigos con la experiencia relevante hasta ahora. Es mi ÚLTIMA CARACTERÍSTICA antes de lanzar mi APLICACIÓN MUY PRIMERA, ¡así que me estoy volviendo loca por haberme quedado atascada con el final a la vista!

Mi arrastrar y soltar funciona perfectamente si suelto el objeto en un área aceptable. Sin embargo, si se suelta en otro lugar, no obtengo un evento y no puedo limpiar después de la operación fallida de arrastrar y soltar. Necesito el evento ACTION_DRAG_ENDED para disparar. Sin embargo, esto solo se dispara (según la documentación) si respondo a ACTION_DRAG_STARTED con un valor de verdadero (lo que significa que puede aceptar el objeto). ¡El problema es que ACTION_DRAG_STARTED no se está disparando!

Así que creo que el quid de la cuestión es que no obtengo ACTION_DRAG_STARTED. Pongo lo que debería ser todo el código de arrastre relevante (o al menos casi todo) aquí:

private OnTouchListener onTouchListener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { Item current = (Item) ((View) v.getTag()).getTag(); //ClipData data = ClipData.newPlainText("test", "testText"); View dragView = (View) v.getTag(); DragShadowBuilder shadowBuilder = new ItemDragShadowBuilder(dragView, new Point(v.getWidth()/2, v.getHeight()/2)); dragSource = dragView; v.startDrag(null, shadowBuilder, current, 0); dragView.setVisibility(View.INVISIBLE); return true; } else { return false; } } }; private static class ItemDragShadowBuilder extends View.DragShadowBuilder { private Point touchPoint = null; private Point sizePoint = null; private View dragView = null; public ItemDragShadowBuilder(View dragView, Point touchPoint) { super(); sizePoint = new Point(dragView.getWidth(), dragView.getHeight()); this.touchPoint = touchPoint; this.dragView = dragView; } @Override public void onProvideShadowMetrics (Point size, Point touch) { size.set(sizePoint.x, sizePoint.y); touch.set(touchPoint.x,touchPoint.y); } @Override public void onDrawShadow(Canvas canvas) { dragView.setBackgroundColor(dragView.getContext().getResources().getColor(R.color.holoBlueDark)); dragView.draw(canvas); //canvas.setBitmap(dragView.getDrawingCache()); //canvas.drawColor(Color.WHITE); //dragView.setAlpha(1); //How do I change the transparency? //super.onDrawShadow(canvas); } } private View dragSource = null; private OnDragListener onDragEventListener = new OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { final int action = event.getAction(); switch (action) { case DragEvent.ACTION_DRAG_STARTED: Toast.makeText(v.getContext(), ((EditText) v.findViewById(R.id.edtParent)).getText().toString() + " started", Toast.LENGTH_SHORT/8).show(); return true; //break;//DRAG AND DROP>>>START DOESN"T FIRE case DragEvent.ACTION_DROP: if ((dragSource != null) && (dragSource != v)) { Item source = (Item) dragSource.getTag(); Item target = (Item) v.getTag(); int sourcePos = source.getPosition(); int targetPos = target.getPosition(); if (targetPos < sourcePos) source.moveUp(sourcePos - targetPos); else source.moveDown(targetPos - sourcePos); dragSource.setVisibility(View.VISIBLE); dragSource = null; fireOnChecklistNeedsResetListener(); return true; } // View view = (View) event.getLocalState(); // Toast.makeText(v.getContext(), ((EditText) v.findViewById(R.id.edtParent)).getText().toString(), Toast.LENGTH_SHORT/8).show(); // Toast.makeText(v.getContext(), ((EditText) view.findViewById(R.id.edtParent)).getText().toString(), Toast.LENGTH_SHORT/8).show(); // Dropped, reassign View to ViewGroup // View view = (View) event.getLocalState(); // ViewGroup owner = (ViewGroup) view.getParent(); // owner.removeView(view); // LinearLayout container = (LinearLayout) v; // container.addView(view); // view.setVisibility(View.VISIBLE); break; case DragEvent.ACTION_DRAG_ENDED: dragSource.setVisibility(View.VISIBLE); dragSource = null; fireOnChecklistNeedsResetListener(); if (!event.getResult()) Toast.makeText(v.getContext(), "UNHANDLED", Toast.LENGTH_SHORT/8).show(); // View vieww = (View) event.getLocalState(); // Toast.makeText(v.getContext(), ((EditText) v.findViewById(R.id.edtParent)).getText().toString(), Toast.LENGTH_SHORT/8).show(); // Toast.makeText(v.getContext(), ((EditText) vieww.findViewById(R.id.edtParent)).getText().toString(), Toast.LENGTH_SHORT/8).show(); break; case DragEvent.ACTION_DRAG_ENTERED: v.setBackgroundColor(v.getContext().getResources().getColor(R.color.holoBlueLight)); return true; //break; case DragEvent.ACTION_DRAG_EXITED: v.setBackgroundColor(v.getContext().getResources().getColor(android.R.color.background_light)); return false; //break; default: break; } return false; } };

Información ampliada: el dragView es un diseño de elemento en una vista de lista (específicamente una vista de lista expandible). La vista que acepta el evento táctil es un pequeño glifo en la vista con un pequeño agarre. Aquí hay una captura de pantalla con una operación de arrastre en curso (tomó un trabajo elegante con los dedos para obtener este lol):

Entonces, si tuviera que colocar el elemento arrastrado en otro elemento (esto los reordena) funciona muy bien. Si lo suelto en otro lugar, no se dispara ningún evento, así que nunca podré limpiarlo. Así, por ejemplo, en la parte superior de la pantalla o si la lista es corta en un área en blanco o en la barra azul en la parte inferior o en cualquiera de las áreas del sistema.

Alternativamente, podría resolver este problema descubriendo una manera de evitar que se arrastre fuera de un área válida, por lo que si sabe cómo hacerlo, esa es otra manera de resolverlo. Mi última opción sería implementar un evento para cada vista posible en la pantalla (de nuevo, estos son diseños de vista de lista, en una vista de lista, que está en un fragmento, que podría estar en varias actividades) por lo que obviamente esto no sería deseable. Y, de hecho, si hago eso, es posible que todavía tenga un problema si se cae en un área de la IU del sistema (como el área de los botones capacitivos o de la barra del sistema) pero no estoy seguro.

En caso de que sea útil, aquí es donde asigno los procedimientos de evento relevantes (eliminé líneas irrelevantes) en el método de elemento de vista de vista de lista de adaptadores:

public View getGroupEdit(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { if (convertView != null) { //irrelevant stuff omitted } else convertView = inf.inflate(R.layout.edit_group_item, null); Item group = (Item) getGroup(groupPosition); convertView.setTag(group); EditText edtParent = (EditText) convertView.findViewById(R.id.edtParent); edtParent.setTag(convertView); scatterItemText(edtParent); //irrelevant stuff omitted ImageView imgGrip = (ImageView) convertView.findViewById(R.id.imgGrip); imgGrip.setTag(convertView); //irrelevant stuff omitted imgGrip.setOnTouchListener(onTouchListener); convertView.setOnDragListener(onDragEventListener); return convertView; }

Y finalmente, FWIW, este es el diseño XML para la vista de elemento de lista:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="55dip" android:orientation="horizontal" > <ImageView android:id="@+id/imgGrip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:src="@drawable/dark_grip2" android:contentDescription="@string/strReorder"/> <CheckBox android:id="@+id/chkParent" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/imgGrip" android:checked="false"/> <EditText android:id="@+id/edtParent" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_toRightOf="@id/chkParent" android:layout_toLeftOf="@+id/btnInsert" android:textSize="17dip" android:inputType="text" android:hint="@string/strItem"/> <ImageButton android:id="@id/btnInsert" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_toLeftOf="@+id/viewParent" android:src="@drawable/dark_content_new" android:clickable="true" android:hint="@string/strAddItemAbove" android:contentDescription="@string/strAddItemAbove" /> <View android:id="@id/viewParent" android:layout_height="match_parent" android:layout_width="0dp" android:layout_toLeftOf="@+id/btnExpand"/> <ImageButton android:id="@id/btnExpand" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_alignParentRight="true" android:src="@drawable/dark_add_sub_item" android:clickable="true" android:hint="@string/strViewSubItems" android:contentDescription="@string/strViewSubItems"/> </RelativeLayout>

¡Muchas gracias por cualquier ayuda!


Esto puede estar relacionado con este problema:

https://code.google.com/p/android/issues/detail?id=25073

Reemplazar la función dispatchDragEvent como se sugiere en ese enlace funcionó para mí:

@Override public boolean dispatchDragEvent(DragEvent ev){ boolean r = super.dispatchDragEvent(ev); if (r && (ev.getAction() == DragEvent.ACTION_DRAG_STARTED || ev.getAction() == DragEvent.ACTION_DRAG_ENDED)){ // If we got a start or end and the return value is true, our // onDragEvent wasn''t called by ViewGroup.dispatchDragEvent // So we do it here. onDragEvent(ev); } return r; }


Las vistas que necesitan ser notificadas de eventos de arrastre deben registrar OnDragListeners . Si necesitan saber cuándo se mueven cosas o tipos específicos de cosas, por ejemplo, para cambiar su apariencia (¡ya sea a "¡ALÉJEME AQUÍ!" O "¡NO SE ATREVES A ATROCARLO EN MÍ!"). Incluso si su vista no es un lugar válido para soltar el objeto que se está arrastrando, si necesita ser ''consciente del arrastre de contexto'' por así decirlo, registra un OnDragListener y devuelve true para ACTION_DRAG_STARTED .

Lo único que me falta de tu pregunta es: ¿qué es exactamente lo que necesitas limpiar? Si solo necesita hacer algunas cosas genéricas que están en el ámbito de la actividad, puede considerar agregar un OnDragListener diferente al RelativeLayout base que se registra para los eventos pero espera que ACTION_DRAG_ENDED a la actividad para que realice el código de limpieza genérico.