Wednesday, March 15, 2017

Can't drag and drop a simple imageView in android studio

Leave a Comment

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

}

3 Answers

Answers 1

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.

Answers 2

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

Answers 3

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.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment