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.