studio soltar example drop arrastrar and android android-layout drag-and-drop imageview

example - arrastrar y soltar android studio



No se puede arrastrar y soltar una imagen simple en Android Studio (3)

Estoy tratando de crear una simple imagen de arrastrar y soltar en el estudio de Android. Puedo hacer que la imagen se arrastre por la pantalla, desaparece tan pronto como la suelte. En la consola, obtengo un "Resultado de caída de informes: falso"

Aquí está mi código:

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

}


Así es como lo hago en mi aplicación:

Para la "vista" que desea arrastrar, establezca esto en 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; } }

Y aquí hay un RelativeLayout que tiene la "vista" de destino establecida en el medio y escucha los eventos de arrastrar y soltar:

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

Este es el detector de arrastre para ImageView en 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; } }

Espero que ayude.


Editado: modificado con un ejemplo práctico de cómo mover todas las vistas contenidas en un RelativeLayout usando onTouch. Creo que el evento onDrag se aplica mejor al arrastrar y soltar elementos de datos, no a mover vistas.

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


Hay un problema con el código dado que será fácil de arreglar. El OnDragListener() debe configurarse para la vista de destino (o cualquier destino posible). Así que OnLongClickListener() y OnTouchListener() están configurados en la vista de origen, imageView , entonces debería haber imageView2 para OnDragListener() . Esa es una explicación larga, pero debería ser una solución fácil.

Ver drag-drop para un buen ejemplo.

El resto de la solución es mucho más complicado si se hace correctamente (sin embargo, hay una solución).

Al iniciar un arrastre, básicamente necesita copiar la imagen al portapapeles y luego pegarla en la vista desplegable. Esto requiere crear un ContentProvider luego usar un ContentResolver ( link ).

Documentación del portapapeles: ClipData

Tengo planes futuros para hacer esto para una aplicación, pero eso no sucederá pronto, así que, desafortunadamente, no podré proporcionar ningún código.

Sin embargo, hay una solución que está mucho menos involucrada.

Coloque etiquetas en cualquier imagen que potencialmente se moverá.

imageView.setTag("ImageTag1");

En onLongClick() , establezca item y dragData como se hace en la pregunta.

Luego, en OnDragListener() , lea la etiqueta, determine qué imagen se está eliminando y luego establezca esa imagen en la vista. Algo como esto:

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;

También debes hacer algo similar en el caso "ACTION_DRAG_EXITED". Si la imagen se coloca en un área no válida, la imagen vuelve a la vista original.