Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Drag and Drop ACTION_DRAG_ENDED not firing

I'm really having a time figuring this one out and can't find any friends with the relevant experience so far. It's my VERY LAST FEATURE before I release my VERY FIRST APP so it's driving me crazy to have gotten bogged down with it with the end in sight!

My drag and drop works perfectly if I drop the object in an acceptable area. However, if dropped somewhere else I do not get an event and cannot clean up after the failed drag drop operation. I need the ACTION_DRAG_ENDED event to fire. However this only fires (according to the documentation) if I respond to ACTION_DRAG_STARTED with a value of true (meaning it can accept the object). The problem is ACTION_DRAG_STARTED is not firing!

So I believe the crux of the matter is that I'm not getting ACTION_DRAG_STARTED. I'm putting what should be all of the relevant dragdrop code (or at least nearly all) here:

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

};

Extended information: The dragView is an item layout in a listview (specifically an expandablelistview). The view that accepts the touch event is a small glyph on the view with a small grip. Here is a screenshot with a drag operation in progress (took some fancy finger work to get this lol):

Drag/Drop Screen

So if I were to drop the dragged item on another item (this reorders them) it works great. If I drop it somewhere else no event fires so I can never clean it up. So for example in the upper part of the screen or if the list is short in a blank area or the blue bar on bottom or any of the system areas.

I could alternatively solve this problem by figuring out a way to prevent dragging away from a valid area so if you know how to do that, that's another way to solve it. My last option would be to implement an event for every possible view on the screen (again these are list view layouts, on a listview, which is on a fragment, which could be on various activities) so obviously this would not be desirable. And in fact if I do that I still may have a problem if it's dropped in a system UI area (like systembar or capacitive buttons area) but I'm not sure.

In case it is helpful here is where I assign the relevant event procedures (I removed irrelevant lines) in the list view adapters get view item method:

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

And finally FWIW, this is the layout XML for the list item view:

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

Thanks so much for any help!

like image 631
Geeks On Hugs Avatar asked Aug 29 '12 18:08

Geeks On Hugs


People also ask

Does Android support drag and drop?

The Android drag and drop framework enables you to add interactive drag and drop capabilities to your app. With drag and drop, users can copy or move text, images, objects—any content that can be represented by a URI—from one View to another within an app or, in multi-window mode, between apps.

What is Action_drag_ended meant for?

The system sends the View object's listener a drag event with action type ACTION_DROP. Ended − Just after the action type ACTION_DROP, the system sends out a drag event with action type ACTION_DRAG_ENDED to indicate that the drag operation is over.

How does drag and drop work on Android?

A drag and drop operation starts when the user makes a UI gesture that your app recognizes as a signal to start dragging data. In response, the app notifies the system that a drag and drop operation is starting. The system calls back to your app to get a representation of the data being dragged (a drag shadow).


1 Answers

Views that need to be notified of drag events must register OnDragListeners. If they need to be aware when specific things or types of things move, for instance in order to change their appearance (either to a "DRAG ME HERE!" or a "DON'T YOU DARE DROP THAT ON ME!"). Even if your view is not a valid place to drop the object being dragged, if it needs to be 'drag context aware' so to speak, it registers an OnDragListener and returns true forACTION_DRAG_STARTED.

The one thing I'm missing from your very thorough question is what exactly you need to be cleaned up? If you just need to do some generic things that are in the scope of the activity, you might consider adding a different OnDragListener to the base RelativeLayout that registers for the events but waits for ACTION_DRAG_ENDED to alert the activity to perform your generic cleanup code

like image 137
JRaymond Avatar answered Oct 04 '22 13:10

JRaymond