lunes, 24 de diciembre de 2012

Tutorial Android: Drag and Drop (Arrastrar y soltar)

Buenos días, hoy vamos a aprender como hacer el efecto de Drag and Drop ( arrastrar y soltar ) un objeto, en este caso una imagen, sobre una superficie en Android.

Este tutorial es bastante simple, consta de un RelativeLayout que hemos llamado 'marco' el cual ocupa toda la pantalla y un ImageView que es el objeto que arrastraremos con el dedo. El marco se podría hacer más pequeño y la imagen solo se arrastraría sobre esa superficie. También se podrían añadir las objetos y mover el deseado. En mi caso trabajo con un Samsung Galaxy S3 y podría mover hasta 5 objetos a la vez ya que reconoce 5 puntos táctiles a la vez. Sin embargo podríamos poner tantos objetos como quisiéramos aunque habría que mover los de uno en uno o en grupos pequeños.




Primerpo os presento como sería el código .XML como veis solo consta de un objeto, RelativeLayout:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" 
    android:id="@+id/marco">
</RelativeLayout>

Una vez tengamos este archivo listo vamos al código Java como siempre totalmente comentado. Si hubiera alguna duda, no olvidéis en preguntar en el apartado de Comentarios:

//Implementamos directamente el Listener a la clase
public class MainActivity extends Activity implements View.OnTouchListener{
 //Definimos el marco por el cual podemos arrastrar la imagen
 private ViewGroup marco;
 //Definimos la imagen que vasmo arrastrar
 private ImageView imagen;
 //Variables para centrar la imagen bajo el dedo
 private int xDelta;
 private int yDelta;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //Relacionamos
  marco = (ViewGroup)findViewById(R.id.marco);
  //Creamos la imagen
  imagen = new ImageView(this);
  //Señalamos la imagen a mostrar
  imagen.setImageResource(R.drawable.ic_launcher);
  //Añadimos el Listener de la clase
  imagen.setOnTouchListener(this);
  //Añadimos la imagen al marco
  marco.addView(imagen);
 }
 //Al tocar la pantalla...
 public boolean onTouch(View view, MotionEvent event) {
  //Recogemos las coordenadas del dedo
  final int X = (int) event.getRawX();
  final int Y = (int) event.getRawY();
  //Dependiendo de la accion recogida..
  switch (event.getAction() & MotionEvent.ACTION_MASK) {
      //Al tocar la pantalla
     case MotionEvent.ACTION_DOWN:
         //Recogemos los parametros de la imagen que hemo tocado
         RelativeLayout.LayoutParams Params = 
            (RelativeLayout.LayoutParams) view.getLayoutParams();
         xDelta = X - Params.leftMargin;
         yDelta = Y - Params.topMargin;
         break;
     case MotionEvent.ACTION_UP:
         //Al levantar el dedo simplemento mostramos un mensaje
         Toast.makeText(this, "Soltamos", Toast.LENGTH_LONG).show();
         break;
     case MotionEvent.ACTION_POINTER_DOWN:
      //No hace falta utilizarlo
         break;
      case MotionEvent.ACTION_POINTER_UP:
       //No hace falta utilizarlo
          break;
      case MotionEvent.ACTION_MOVE:
       //Al mover el dedo vamos actualizando 
                        //los margenes de la imagen para
       //crear efecto de arrastrado
          RelativeLayout.LayoutParams layoutParams = 
             (RelativeLayout.LayoutParams) view.getLayoutParams();
          layoutParams.leftMargin = X - xDelta;
          layoutParams.topMargin = Y - yDelta;
          //Qutamos un poco de margen para 
                        //que la imagen no se deforme
          //al llegar al final de la pantalla y pueda ir más allá
          //probar también el codigo omitiendo estas dos líneas
          layoutParams.rightMargin = -50;
          layoutParams.bottomMargin = -50;
          //le añadimos los nuevos 
                        //parametros para mover la imagen
          view.setLayoutParams(layoutParams);
          break;
      }
   //Se podría decir que 'dibujamos' 
                        //la posición de la imagen en el marco.
      marco.invalidate();
      return true;
  }}

Publico este vídeo para que podáis ver el resultado:



Muchas gracias por todo! no olvidéis comentar!

21 comentarios:

  1. Buen blog, Victor,

    ¿tienes idea de como se podría hacer para mover todos los elementos del viewGroup simultaneamente? (Por ej. un par de TextView y un ImageView a la vez)

    Saludos

    ResponderEliminar
    Respuestas
    1. No he probado, pero supongo que si metes todo lo que quieres mover a la vez en un layout y lo tratas como a la imagen de este tutorial lo haría.

      Eliminar
    2. No sé si llego a tiempo, o qué... pero si haces esto funciona para mover varios objetos a la vez... además el LinearLayout se repite... por que así creas dos intancias, con lo que consigues mover (en este caso) dos objetos por un lado y otros dos por otro:
      LinearLayout linear;

      linear = new LinearLayout(this);
      //linear.set
      // Relacionamos
      marco = (ViewGroup) findViewById(R.id.marco);
      // Creamos la imagen
      imagen = new ImageView(this);
      // Señalamos la imagen a mostrar
      imagen.setImageResource(R.drawable.ic_launcher);
      linear.addView(imagen);

      // Creamos la imagen
      imagen = new ImageView(this);
      // Señalamos la imagen a mostrar
      imagen.setImageResource(R.drawable.bt_info);
      linear.addView(imagen);

      // Añadimos el Listener de la clase
      linear.setOnTouchListener(this);
      // Añadimos la imagen al marco
      marco.addView(linear);

      linear = new LinearLayout(this);
      // Creamos la imagen
      imagen = new ImageView(this);
      // Señalamos la imagen a mostrar
      imagen.setImageResource(R.drawable.ic_launcher);
      linear.addView(imagen);

      // Creamos la imagen
      imagen = new ImageView(this);
      // Señalamos la imagen a mostrar
      imagen.setImageResource(R.drawable.bt_info);
      linear.addView(imagen);

      // Añadimos el Listener de la clase
      linear.setOnTouchListener(this);
      // Añadimos la imagen al marco
      marco.addView(linear);

      Eliminar
  2. Hola, muy buen y simple tutorial.
    Mi duda es cómo saber en qué posición se ha soltado la imagen (trabajo con la API 8, por lo que no puedo utilizar view.getX() o view.getY()).

    Gracias

    ResponderEliminar
  3. Fenomeno. código muy comentado y simple. Gracias!

    ResponderEliminar
  4. El código y tutorial, muy bien, pero me duelen los ojos desde que leí uviera en lugar de hubiera...
    gracias de todos modos

    ResponderEliminar
  5. Buenisimo tutorial!!!!
    Una pregunta... ¿como hago si tengo dos imagenes que cuando haga el ACTION_DOWN, la que seleccione quede siempre por encima de la otra cuando la mueva? Muchas gracias! Un saludo

    ResponderEliminar
  6. Buenas. ¿como podria guardar la nueva posicion en un XML y que lo leyera correctamente la proxima vez que se ejecute?

    ResponderEliminar
  7. Hola,

    Muy interesante el código y su explicación. Vereis estoy haciendo un ejercicio en el que tengo como imagen de fondo un semáforo, entonces me gustaría hacer que según pulses en una luz o en otra aparezca una pantalla con un mensaje o con otro, pero no sé cómo hacerlo, es decir ¿cómo puedo saber que estoy tocando la parte de la imagen referente a la luz roja?

    Muchas, gracias.

    Un saludo.

    ResponderEliminar
  8. Y si quiera poder ampliar y reducir la imagen como seria?
    por separado puedo hacerlo, es decir muevo bien y hago zoom con los dedos pero si junto todo no funciona.

    ResponderEliminar
  9. Útil y fácil, gracias ;)

    ResponderEliminar
  10. muy buen tutorial..pero disculpa, soy principiante..me gustaría saber el codigo para arrastrar una imagen a un contenedor y una vez estando en contenedor arroje una imagen como respuesta..me podrias ayudar por favor.

    ResponderEliminar
  11. public class MainActivity extends ActionBarActivity implements OnTouchListener {

    TextView text;
    ViewGroup marco;
    ImageView imagen;

    private int xDelta;
    private int yDelta;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    text = (TextView) findViewById(R.id.coordenada);
    marco = (ViewGroup) findViewById(R.id.container);
    imagen = new ImageView(this);
    imagen.setImageResource(R.drawable.ic_launcher);
    imagen.setOnTouchListener(this);
    marco.addView(imagen);
    }

    public boolean onTouch(View view, MotionEvent event){
    final int X = (int) event.getRawX();
    final int Y = (int) event.getRawY();
    switch(event.getAction() & MotionEvent.ACTION_MASK){
    case MotionEvent.ACTION_UP:
    Toast.makeText(this, "Soltamos", Toast.LENGTH_LONG).show();
    break;
    case MotionEvent.ACTION_DOWN:
    RelativeLayout.LayoutParams Params = (RelativeLayout.LayoutParams) view.getLayoutParams();
    xDelta = X - Params.leftMargin;
    yDelta = Y - Params.topMargin;
    break;
    case MotionEvent.ACTION_MOVE:
    RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
    layoutParams.leftMargin = X - xDelta;
    layoutParams.topMargin = Y - yDelta;
    text.setText("X:"+(X - xDelta)+";Y:"+(Y - yDelta));
    view.setLayoutParams(layoutParams);
    break;
    }
    marco.invalidate();
    return true;
    }

    ResponderEliminar
  12. public class MainActivity extends Activity implements OnTouchListener{
    //Definimos el marco por el cual podemos arrastrar la imagen
    private ViewGroup marco;
    //Definimos la imagen que vasmo arrastrar
    private ImageView imagen;
    //Variables para centrar la imagen bajo el dedo
    private int xDelta;
    private int yDelta;

    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //Relacionamos
    marco = (ViewGroup)findViewById(R.id.marco);
    tv = (TextView) findViewById(R.id.textView1);
    //Creamos la imagen
    imagen = new ImageView(this);
    //Señalamos la imagen a mostrar
    imagen.setImageResource(R.drawable.ic_launcher);
    //Añadimos el Listener de la clase
    imagen.setOnTouchListener(this);
    //Añadimos la imagen al marco
    marco.addView(imagen);
    }
    //Al tocar la pantalla...
    public boolean onTouch(View view, MotionEvent event) {
    //Recogemos las coordenadas del dedo
    final int X = (int) event.getRawX();
    final int Y = (int) event.getRawY();
    //Dependiendo de la accion recogida..
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
    //Al tocar la pantalla
    case MotionEvent.ACTION_DOWN:
    //Recogemos los parametros de la imagen que hemo tocado
    RelativeLayout.LayoutParams Params =
    (RelativeLayout.LayoutParams) view.getLayoutParams();
    xDelta = X - Params.leftMargin;
    yDelta = Y - Params.topMargin;
    break;
    case MotionEvent.ACTION_UP:
    //Al levantar el dedo simplemento mostramos un mensaje
    Toast.makeText(this, "Soltamos", Toast.LENGTH_LONG).show();
    break;
    case MotionEvent.ACTION_POINTER_DOWN:
    //No hace falta utilizarlo
    break;
    case MotionEvent.ACTION_POINTER_UP:
    //No hace falta utilizarlo
    break;
    case MotionEvent.ACTION_MOVE:
    //Al mover el dedo vamos actualizando
    //los margenes de la imagen para
    //crear efecto de arrastrado
    RelativeLayout.LayoutParams layoutParams =
    (RelativeLayout.LayoutParams) view.getLayoutParams();
    layoutParams.leftMargin = X - xDelta;
    layoutParams.topMargin = Y - yDelta;
    //Qutamos un poco de margen para
    //que la imagen no se deforme
    //al llegar al final de la pantalla y pueda ir más allá
    //probar también el codigo omitiendo estas dos líneas
    layoutParams.rightMargin = -50;
    layoutParams.bottomMargin = -50;
    //le añadimos los nuevos
    //parametros para mover la imagen
    view.setLayoutParams(layoutParams);
    tv.setText("X:"+X+" Y:"+Y);
    break;
    }
    //Se podría decir que 'dibujamos'
    //la posición de la imagen en el marco.
    marco.invalidate();
    return true;
    }}

    ResponderEliminar
  13. Muy Buen tuto, explicas perfectamente en cada método lo que hace.

    pero, ¿si quisiera moverla al tocar sobre la pantalla?, que si toco a la derecha de la imagen, se desplace a la derecha.

    ResponderEliminar
  14. Muito Obrigado por ajudar!! Estava procurando por isso faz dias.
    Obrigado!

    ResponderEliminar
  15. como hago para ajustar la imagen a mi imageView

    ResponderEliminar