Android Drag and Drop ACTION_DRAG_ENDED не срабатывает

У меня действительно есть время понять это и не могу найти друзей с соответствующим опытом. Это моя ОЧЕНЬ ПОСЛЕДНЯЯ ХАРАКТЕРИСТИКА, прежде чем я выпущу свой ОЧЕНЬ ПЕРВЫЙ APP, так что это сводит меня с ума, чтобы загнать его с концом в поле зрения!

Моя перетаскивание отлично работает, если я отбрасываю объект в приемлемой области. Однако, если вы уронили в другое место, я не получаю событие и не могу выполнить очистку после неудачной операции перетаскивания. Мне нужно событие ACTION_DRAG_ENDED для запуска. Однако это только срабатывает (согласно документации), если я отвечу ACTION_DRAG_STARTED со значением true (что означает, что он может принять объект). Проблема в ACTION_DRAG_STARTED не срабатывает!

Поэтому я считаю, что суть в том, что я не получаю ACTION_DRAG_STARTED. Я помещаю то, что должно быть всем соответствующим кодом dragdrop (или, по крайней мере, почти всем) здесь:

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;
    }

};

Расширенная информация: dragView - это макет элемента в списке (в частности, расширение). Представление, которое принимает событие касания, является небольшим глифом на представлении с небольшим захватом. Вот скриншот с текущей операцией перетаскивания (для получения этого LOL требуется несколько полезных действий):

Drag/Drop Screen

Поэтому, если я должен был перетащить перетаскиваемый элемент на другой элемент (это переупорядочивает их), он отлично работает. Если я отброшу его в другое место, события не срабатывают, поэтому я никогда не смогу его очистить. Так, например, в верхней части экрана или если короткий список в пустой области или синяя полоска внизу или в любой из системных областей.

Я мог бы альтернативно решить эту проблему, выяснив способ предотвратить перетаскивание из допустимой области, поэтому, если вы знаете, как это сделать, это еще один способ ее решения. Моим последним вариантом было бы реализовать событие для каждого возможного представления на экране (опять же это макеты списка, в списке, который находится на фрагменте, который может быть на разных видах деятельности), поэтому, очевидно, это было бы нежелательно. И на самом деле, если я это сделаю, у меня все еще может быть проблема, если она упадет в области пользовательского интерфейса системы (например, в системной панели или в области емкостных кнопок), но я не уверен.

В случае полезности здесь я назначаю соответствующие процедуры событий (я удаляю ненужные строки) в адаптерах просмотра списка, получая метод элемента представления:

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;
}

И, наконец, FWIW, это формат XML для представления элемента списка:

<?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>

Большое спасибо за любую помощь!

Ответы

Ответ 1

Представления, которые должны быть уведомлены о событиях перетаскивания, должны регистрироваться OnDragListeners. Если им нужно знать, когда движутся конкретные вещи или типы вещей, например, чтобы изменить их внешний вид (либо на "DRAG ME ЗДЕСЬ!", Либо "НЕ СДЕЛАЙТЕ УБЕДИТЕСЬ, ЧТО НА МЕНЯ!" ). Даже если ваше представление не является допустимым местом для перетаскивания объекта, если он должен быть "перетаскивать контекст", так сказать, он регистрирует OnDragListener и возвращает true для ACTION_DRAG_STARTED.

Единственное, что мне не хватает из вашего очень тщательного вопроса, - это то, что именно нужно очистить? Если вам просто нужно сделать некоторые общие вещи, которые входят в сферу действия, вы можете подумать о добавлении другого OnDragListener к базе RelativeLayout, которая регистрируется для событий, но ждет ACTION_DRAG_ENDED, чтобы предупредить активность для выполнения ваш общий код очистки

Ответ 2

Это может быть связано с этой проблемой:

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

Переопределение функции dispatchDragEvent, предложенная в этой ссылке, работало для меня:

@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;
}