17

I'm trying to create a simple drag and drop image in android studio. I can get the image to drag around the screen, it disappears as soon as I release it. In the console, I get a "Reporting drop result: false"

Here's my code:

ImageView mImageView;
String mString;

private android.widget.RelativeLayout.LayoutParams mLayoutParams;

    mImageView.setOnLongClickListener(new View.OnLongClickListener(){
        @Override
        public boolean onLongClick(View v){
            ClipData.Item item = new ClipData.Item((CharSequence)v.getTag());
            String[] mimeTypes = {
                    ClipDescription.MIMETYPE_TEXT_PLAIN
            };
            ClipData dragData = new ClipData(v.getTag().toString(), mimeTypes, item);
            View.DragShadowBuilder myShadow = new View.DragShadowBuilder(mImageView);

            v.startDrag(dragData, myShadow, null, 0);
            return true;
        }
    });

    mImageView.setOnDragListener(new View.OnDragListener() {
        @Override
        public boolean onDrag(View v, DragEvent event) {
            switch(event.getAction()) {
                case DragEvent.ACTION_DRAG_STARTED:
                    mLayoutParams = (RelativeLayout.LayoutParams)v.getLayoutParams();
                    Log.d(mString, "Action is DragEvent.ACTION_DRAG_STARTED");

                    // Do nothing
                    break;

                case DragEvent.ACTION_DRAG_ENTERED:
                    Log.d(mString, "Action is DragEvent.ACTION_DRAG_ENTERED");
                    int x_cord = (int) event.getX();
                    int y_cord = (int) event.getY();
                    break;

                case DragEvent.ACTION_DRAG_EXITED :
                    Log.d(mString, "Action is DragEvent.ACTION_DRAG_EXITED");
                    x_cord = (int) event.getX();
                    y_cord = (int) event.getY();
                    mLayoutParams.leftMargin = x_cord;
                    mLayoutParams.topMargin = y_cord;
                    v.setLayoutParams(mLayoutParams);
                    break;

                case DragEvent.ACTION_DRAG_LOCATION  :
                    Log.d(mString, "Action is DragEvent.ACTION_DRAG_LOCATION");
                    x_cord = (int) event.getX();
                    y_cord = (int) event.getY();
                    break;

                case DragEvent.ACTION_DRAG_ENDED   :
                    Log.d(mString, "Action is DragEvent.ACTION_DRAG_ENDED");

                    // Do nothing
                    break;

                case DragEvent.ACTION_DROP:
                    Log.d(mString, "ACTION_DROP event");

                    // Do nothing
                    break;
                default: break;
            }
            return true;
        }
    });

    mImageView.setOnTouchListener(new View.OnTouchListener(){
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                ClipData data = ClipData.newPlainText("", "");
                View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(mImageView);

                mImageView.startDrag(data, shadowBuilder, mImageView, 0);
                mImageView.setVisibility(View.INVISIBLE);
                return true;
            } else {
                return false;
            }
        }
    });
}

}

Rob
  • 2,243
  • 4
  • 29
  • 40
fmi
  • 512
  • 4
  • 14

3 Answers3

8

Edited : Changed with a working example of how to move all the views contained in a RelativeLayout using onTouch. I think that onDrag event applies better to drag and drop data items, not to move views.

public class MainActivity extends AppCompatActivity implements View.OnTouchListener {
    private RelativeLayout mRelLay;
    private float mInitialX, mInitialY;
    private int mInitialLeft, mInitialTop;
    private View mMovingView = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRelLay = (RelativeLayout) findViewById(R.id.relativeLayout);

        for (int i = 0; i < mRelLay.getChildCount(); i++)
            mRelLay.getChildAt(i).setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        RelativeLayout.LayoutParams mLayoutParams;

        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mMovingView = view;
                mLayoutParams = (RelativeLayout.LayoutParams) mMovingView.getLayoutParams();
                mInitialX = motionEvent.getRawX();
                mInitialY = motionEvent.getRawY();
                mInitialLeft = mLayoutParams.leftMargin;
                mInitialTop = mLayoutParams.topMargin;
                break;

            case MotionEvent.ACTION_MOVE:
                if (mMovingView != null) {
                    mLayoutParams = (RelativeLayout.LayoutParams) mMovingView.getLayoutParams();
                    mLayoutParams.leftMargin = (int) (mInitialLeft + motionEvent.getRawX() - mInitialX);
                    mLayoutParams.topMargin = (int) (mInitialTop + motionEvent.getRawY() - mInitialY);
                    mMovingView.setLayoutParams(mLayoutParams);
                }
                break;

            case MotionEvent.ACTION_UP:
                mMovingView = null;
                break;
        }

        return true;
    }
}
from56
  • 3,976
  • 2
  • 13
  • 23
5

There's one problem with the code that's given that will be easy to fix. The OnDragListener() should be set for the destination (or any possible destination) view. So OnLongClickListener() and OnTouchListener() are set on the source view, imageView then there should be imageView2 for the OnDragListener(). That's a long explanation but should be an easy fix.

See drag-drop for a good example.

The rest of the solution is much more involved if done properly (however, there's a work around).

When starting a drag, basically you need to copy the image to the clipboard, then paste it in the drop view. This requires creating a ContentProvider then using a ContentResolver (link).

Clipboard documentation: ClipData

I have future plans to do this for an app but that won't be happening any time soon so, unfortunately, I won't be able to provide any code.

However, there is a workaround that's much less involved.

Set tag(s) on any image that will potentially be moved.

imageView.setTag("ImageTag1");

In onLongClick(), set item and dragData as is done in the question.

Then in OnDragListener(), read the tag, determine which image is being dropped then set that image to the view. Something like this:

case DragEvent.ACTION_DROP:
    ClipData.Item item = event.getClipData().getItemAt(0);
    CharSequence dragData = item.getText();

    if(dragData.equals("ImageTag1")) {
        // this gets jpg image from "drawable" folder,
        //      set ImageView appropriately for your usage
        ((ImageView)v).setImageResource(R.drawable.image1);                            
    } else if(dragData.equals("ImageTag2")) {
        ((ImageView)v).setImageResource(R.drawable.image2);
    }
    break;

You also need to do something similar in case "ACTION_DRAG_EXITED". If the image is dropped in an invalid area, this puts the image back to the original view.

Community
  • 1
  • 1
Gary99
  • 1,750
  • 1
  • 19
  • 33
0

This is how I do it in my app:

For the "view" that you want to drag, set this in the onTouchListener:

    public final class ChoiceTouchListener implements OnTouchListener {
    Context context;
    //int index;
    public static float offsetX = 0,offsetY = 0;

    DragShadowBuilder shadowBuilder;

    public ChoiceTouchListener(Context context) {
        super();
        this.context = context;
        //this.index = index;
    }




    public boolean onTouch(View view, MotionEvent motionEvent) {
        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {

            //view.setTag("option"+index);
            ClipData data = ClipData.newPlainText("tag", view.getTag().toString());
            shadowBuilder = new View.DragShadowBuilder(view);

            //start dragging the item touched
            view.startDrag(data, shadowBuilder, view, 0);

            offsetX = view.getLeft();//(int)view.getX();//(int)motionEvent.getX();
            offsetY = view.getTop();//(int)view.getY();//motionEvent.getY();
            view.setVisibility(View.INVISIBLE);
            Log.v("here","it is ::" + (int)motionEvent.getX() + " , "+(int)motionEvent.getY());

            return false;



        }

        return true;

    }
}

And here's a RelativeLayout that has the destination "view" set in the middle and listens for the drag and drop events:

    public class DragLayout extends RelativeLayout {

    boolean DEBUG = true;

    AnimationDrawable blenderAnim;
    Handler handlerAnim2;
    Context context;

    private int dimensionInPixel = 200;
    int screenWidth,screenHeight;

    public DragLayout(Context context) {
        super(context);

        this.context = context;

        //not to include in main program
        getDimensionsofScreen();

        setLayout();
        setViews();

    }



    private void setLayout() {


        // set according to parent layout (not according to current layout)
        RelativeLayout.LayoutParams rLp = new RelativeLayout.LayoutParams(
                LayoutParams.MATCH_PARENT,  LayoutParams.MATCH_PARENT);
        rLp.topMargin = 2 * (screenHeight / 25); // calculating 1/10 of 4/5
        // screen


        this.setLayoutParams(rLp);

    }

    void setViews() {

        ImageView img2 = new ImageView(context);

        int dimensionInDp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dimensionInPixel, getResources().getDisplayMetrics());

        RelativeLayout.LayoutParams rLp = new RelativeLayout.LayoutParams(
                (screenWidth / 5), (screenHeight / 5));
        rLp.topMargin = (screenHeight / 10);
        rLp.leftMargin = (4*screenWidth / 10);
        rLp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);

        img2.setLayoutParams(rLp);
        img2.getLayoutParams().height = dimensionInDp;
        img2.getLayoutParams().width = dimensionInDp;
        img2.setImageDrawable(getResources().getDrawable(R.drawable.blender_anim));
        img2.setOnDragListener(new ChoiceDragListener(context));
        this.addView(img2);

        blenderAnim = (AnimationDrawable)img2.getDrawable();
        blenderAnim.setOneShot(true);
        blenderAnim.stop();

    }


    public ArrayList<Integer> getDimensionsofScreen() {

        //metrics that holds the value of height and width
        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();;
        ArrayList<Integer> vals = new ArrayList<Integer>();

        vals.add(displayMetrics.widthPixels);
        vals.add(displayMetrics.heightPixels);
        screenHeight = displayMetrics.heightPixels;
        screenWidth = displayMetrics.widthPixels;

        return vals;
    }



    @SuppressLint("NewApi")
    @Override
    public boolean onDragEvent(DragEvent event) {

        int mCurX = (int) event.getX();
        int mCurY = (int) event.getY();

        if(event.getAction() == DragEvent.ACTION_DRAG_STARTED || event.getAction() == DragEvent.ACTION_DRAG_ENTERED) {
            if (blenderAnim.isRunning()) {
                blenderAnim.stop();
            } else {
                blenderAnim.run();

                handlerAnim2 = new Handler();
                handlerAnim2.postDelayed(
                        new Runnable(){

                            @Override
                            public void run() {
                                blenderAnim.stop();

                            }},
                        getAnimationDuration(blenderAnim));
            }
        }

        if(event.getAction() == DragEvent.ACTION_DROP || event.getAction() == DragEvent.ACTION_DRAG_EXITED) {

            if (blenderAnim.isRunning()) {
                blenderAnim.stop();
            } else {
                blenderAnim.run();

                handlerAnim2 = new Handler();
                handlerAnim2.postDelayed(
                        new Runnable(){

                            @Override
                            public void run() {
                                blenderAnim.stop();

                            }},
                        getAnimationDuration(blenderAnim));
            }

            Log.v("here", "it is :: " + mCurX + ", " + mCurY);

            View view1 = (View) event.getLocalState();
            view1.setVisibility(View.VISIBLE);
            ObjectAnimator animationx = ObjectAnimator.ofFloat(view1,"translationX", mCurX - ChoiceTouchListener.offsetX-(screenWidth / 10),0.0f);
            ObjectAnimator animationy = ObjectAnimator.ofFloat(view1, "translationY", mCurY - ChoiceTouchListener.offsetY - (screenHeight / 10), 0.0f);
            AnimatorSet animSet = new AnimatorSet();
            animSet.setDuration(500);
            animSet.playTogether(animationx,animationy);

            animSet.start();

        }
        if(event.getAction() == DragEvent.ACTION_DROP || event.getAction() == DragEvent.ACTION_DRAG_ENDED){
            if(blenderAnim.isRunning()){
                blenderAnim.stop();
            }
        }
        return true;


    }

    private int getAnimationDuration(AnimationDrawable src){
        int dur = 0;
        for(int i=0; i<src.getNumberOfFrames(); i++){
            dur += src.getDuration(i);
        }
        return dur;
    }
}

This is the drag listener for the ImageView in the DragLayout:

    public class ChoiceDragListener implements View.OnDragListener {

    boolean DEBUG = true;
    Context context;
    public String TAG = "Drag Layout:";

    public ChoiceDragListener(Context context){
        this.context = context;
    }

    @Override
    public boolean onDrag(View v, DragEvent event) {
        switch (event.getAction()) {
            case DragEvent.ACTION_DRAG_STARTED:
                if(DEBUG) Log.v("here","drag started");
                break;
            case DragEvent.ACTION_DRAG_ENTERED:
                break;
            case DragEvent.ACTION_DRAG_LOCATION:
                int mCurX = (int) event.getX();
                int mCurY = (int) event.getY();

                if(DEBUG) Log.v("Cur(X, Y) : " ,"here ::" + mCurX + ", " + mCurY );

                break;
            case DragEvent.ACTION_DRAG_EXITED:

                if(DEBUG)
                    Log.v("here","drag exits");

                break;
            case DragEvent.ACTION_DROP:

                //handle the dragged view being dropped over a drop view
                View view = (View) event.getLocalState();
                ClipData cd =  event.getClipData();
                ClipData.Item item = cd.getItemAt(0);
                String resp = item.coerceToText(context).toString();

                //view dragged item is being dropped on
                ImageView dropTarget = (ImageView) v;

                //view being dragged and dropped
                final ImageView dropped = (ImageView) view;

                dropped.setEnabled(false);

                //if an item has already been dropped here, there will be a tag
                final Object tag = dropTarget.getTag();

                LayoutInflater li = LayoutInflater.from(context);
                View promptsView = li.inflate(R.layout.ns_scoop_dialog, null);

                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                        context);

                // set prompts.xml to alertdialog builder
                alertDialogBuilder.setView(promptsView);

                final EditText userInput = (EditText) promptsView
                        .findViewById(R.id.edit1);

                // set dialog message
                alertDialogBuilder
                        .setIcon(R.mipmap.ic_launcher)
                        .setTitle(dropped.getTag().toString())
                        .setCancelable(false)
                        .setPositiveButton("OK",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog,int id) {
                                        // get user input and set it to result
                                        // edit text
                                        String inAmt = userInput.getText().toString();

                                        CreateSmoothie.nsList.add(inAmt + " Green Scoops " + dropped.getTag().toString());
                                        Log.d(TAG, inAmt + " Green Scoops " + dropped.getTag().toString() + " added to list");

                                        dialog.dismiss();

                                        //dropped.setEnabled(true);
                                    }
                                })
                        .setNegativeButton("Cancel",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int id) {
                                        dialog.cancel();

                                        int existingID = dropped.getId();

                                        //set the original view visible again
                                        ((Activity) context).findViewById(existingID).setVisibility(View.VISIBLE);

                                        dropped.setEnabled(true);

                                    }
                                });

                // create alert dialog
                AlertDialog alertDialog = alertDialogBuilder.create();

                // show it
                alertDialog.show();
                //Button nButton = alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE);
                //nButton.setBackgroundColor(Color.GREEN);
                //Button pButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
                //pButton.setBackgroundColor(Color.GREEN);

                if(tag!=null)
                {
                    //the tag is the view id already dropped here
                    int existingID = (Integer)tag;

                    //set the original view visible again
                    ((Activity) context).findViewById(existingID).setVisibility(View.VISIBLE);
                }

                break;
            case DragEvent.ACTION_DRAG_ENDED:

                if(DEBUG) Log.i("drag event", "ended::" + ChoiceTouchListener.offsetX + "," + ChoiceTouchListener.offsetY);

                /**
                 * returning false so that goes to parentView onDrag function
                 */
                return false;
            //break;
            default:
                break;
        }
        return true;
    }

}

Hope that helps.

Steve C.
  • 1,333
  • 3
  • 19
  • 50