27 votes

Android imageView: définition des paramètres de zoom par glisser et pincer

Je suis actuellement en train de développer pour Android (ma première application) une application qui permet aux utilisateurs de voir le plan du métro et de pouvoir zoomer et faire glisser.

Je suis en train de modifier le code trouvé dans Hello Android, 3ème édition et j'ai réussi à faire fonctionner le zoom et le glisser. J'utilise Matrix comme échelle de mise en page.

Cependant, j'ai maintenant 3 problèmes :

  1. J'ai essayé beaucoup de choses pour limiter les paramètres du glisser mais je n'arrive pas à empêcher l'élément d'être glissé hors de la vue parente (et peut en fait disparaître de la vue). J'ai essayé de définir les paramètres de mise en page dans le fichier XML et cela ne fonctionne tout simplement pas.

  2. Je peux zoomer avec pincement mais j'ai du mal, encore une fois, à limiter la quantité de zoom. J'essaie de jouer avec le paramètre max_zoom et min_zoom pour limiter la valeur d'échelle (je posterai mon code après)

  3. J'ai aussi du mal à mapp une coordonnée sur mon image afin que les gens puissent cliquer sur certaines parties (tout l'intérêt de cela est de permettre aux utilisateurs de cliquer sur une station sur la carte et de consulter des informations à ce sujet)

J'ai l'impression d'avoir des problèmes parce que j'utilise l'échelle de matrice.

Voici mon code actuel :

Touch.java

package org.example.touch;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.GridView;
import android.widget.ImageView;

public class Touch extends Activity implements OnTouchListener {
private static final String TAG = "Touch";

private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 5.0f;

// Ces matrices seront utilisées pour déplacer et zoomer l'image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();

// Nous pouvons être dans l'un de ces 3 états
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;

// Se rappeler de certaines choses pour le zoom
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);
   ImageView view = (ImageView) findViewById(R.id.imageView);
   //view.setLayoutParams(new GridView.LayoutParams(85, 85));
   view.setScaleType(ImageView.ScaleType.FIT_CENTER);
   view.setOnTouchListener(this);   
}

public boolean onTouch(View v, MotionEvent event) {
   ImageView view = (ImageView) v;
   view.setScaleType(ImageView.ScaleType.MATRIX);
   float scale;

   // Enregistrer l'événement tactile dans le log
   dumpEvent(event);

   // Gérer les événements tactiles ici...
   switch (event.getAction() & MotionEvent.ACTION_MASK) {

   case MotionEvent.ACTION_DOWN: //premier doigt en bas seulement
      savedMatrix.set(matrix);
      start.set(event.getX(), event.getY());
      Log.d(TAG, "mode=DRAG" );
      mode = DRAG;
      break;
   case MotionEvent.ACTION_UP: //premier doigt levé
   case MotionEvent.ACTION_POINTER_UP: //deuxième doigt levé
      mode = NONE;
      Log.d(TAG, "mode=NONE" );
      break;
   case MotionEvent.ACTION_POINTER_DOWN: //deuxième doigt en bas
      oldDist = spacing(event);
      Log.d(TAG, "oldDist=" + oldDist);
      if (oldDist > 5f) {
         savedMatrix.set(matrix);
         midPoint(mid, event);
         mode = ZOOM;
         Log.d(TAG, "mode=ZOOM" );
      }
      break;

   case MotionEvent.ACTION_MOVE: 
      if (mode == DRAG) { //mouvement du premier doigt
         matrix.set(savedMatrix);
         if (view.getLeft() >= -392){
            matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
         }
      }
      else if (mode == ZOOM) { //zoom à pincement
         float newDist = spacing(event);
         Log.d(TAG, "newDist=" + newDist);
         if (newDist > 5f) {
            matrix.set(savedMatrix);
            scale = newDist / oldDist; **//penser que je dois jouer avec cette valeur pour la limiter**
            matrix.postScale(scale, scale, mid.x, mid.y);
         }
      }
      break;
   }

   // Effectuer la transformation
   view.setImageMatrix(matrix);

   return true; // indiquer que l'événement a été géré
}

private float spacing(MotionEvent event) {
   float x = event.getX(0) - event.getX(1);
   float y = event.getY(0) - event.getY(1);
   return FloatMath.sqrt(x * x + y * y);
}

private void midPoint(PointF point, MotionEvent event) {
   float x = event.getX(0) + event.getX(1);
   float y = event.getY(0) + event.getY(1);
   point.set(x / 2, y / 2);
}

/** Montrer un événement dans la vue LogCat, pour le débogage */
private void dumpEvent(MotionEvent event) {
   String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" ,
      "POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" };
   StringBuilder sb = new StringBuilder();
   int action = event.getAction();
   int actionCode = action & MotionEvent.ACTION_MASK;
   sb.append("event ACTION_" ).append(names[actionCode]);
   if (actionCode == MotionEvent.ACTION_POINTER_DOWN
         || actionCode == MotionEvent.ACTION_POINTER_UP) {
      sb.append("(pid " ).append(
      action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
      sb.append(")" );
   }
   sb.append("[" );
   for (int i = 0; i < event.getPointerCount(); i++) {
      sb.append("#" ).append(i);
      sb.append("(pid " ).append(event.getPointerId(i));
      sb.append(")=" ).append((int) event.getX(i));
      sb.append("," ).append((int) event.getY(i));
      if (i + 1 < event.getPointerCount())
         sb.append(";" );
   }
   sb.append("]" );
   Log.d(TAG, sb.toString());
}
}

main.xml (assez simple, rien de vraiment compliqué) :

AndroidManifest.xml (seul le thème a été ajouté pour qu'il n'y ait pas de barre de titre et qu'il soit en plein écran) :

16voto

Jason Points 942

Je viens de créer ceci :

https://github.com/jasonpolites/gesture-imageview

Peut-être utile pour quelqu'un...

10voto

Ernie Thomason Points 101

Une autre option qui pourrait fonctionner pour certains est d'utiliser un WebView, qui possède des contrôles de zoom intégrés.

WebView webView = new WebView(this);
webView.setBackgroundColor(0xff000000);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setSupportZoom(true);
//webView.getSettings().setDisplayZoomControls(false);  // API 11
webView.loadDataWithBaseURL(null, getHtml(), "text/html", "UTF-8", null);
mainView.addView(webView, -1, -2);

6voto

MCeley Points 5753

Je sais que c'est vieux mais je cherchais à le faire et j'ai une solution qui fonctionne assez bien. Juste après votre instruction switch et avant d'initialiser la matrice, vous pouvez limiter le zoom comme ceci :

private void limitZoom(Matrix m) {

    float[] values = new float[9];
    m.getValues(values);
    float scaleX = values[Matrix.MSCALE_X];
    float scaleY = values[Matrix.MSCALE_Y];
    if(scaleX > MAX_ZOOM) {
        scaleX = MAX_ZOOM;
    } else if(scaleX < MIN_ZOOM) {
        scaleX = MIN_ZOOM;
    }

    if(scaleY > MAX_ZOOM) {
        scaleY = MAX_ZOOM;
    } else if(scaleY < MIN_ZOOM) {
        scaleY = MIN_ZOOM;
    }

    values[Matrix.MSCALE_X] = scaleX;
    values[Matrix.MSCALE_Y] = scaleY; 
    m.setValues(values);
}

Je travaille encore sur la limitation de la translation mais cela devrait fonctionner pour la limitation du zoom.

ÉDIT : Voici une solution pour limiter la translation. Juste une note, je le fais pour une vue d'image en plein écran, c'est pourquoi j'utilise la largeur et la hauteur de l'écran dans mes facteur de limitation mais vous pourriez tout aussi bien utiliser la largeur et la hauteur de votre vue à la place.

private void limitDrag(Matrix m) {
    float[] values = new float[9];
    m.getValues(values);
    float transX = values[Matrix.MTRANS_X];
    float transY = values[Matrix.MTRANS_Y];
    float scaleX = values[Matrix.MSCALE_X];
    float scaleY = values[Matrix.MSCALE_Y];

    ImageView iv = (ImageView)findViewById(R.id.photo_view);
    Rect bounds = iv.getDrawable().getBounds();
    int viewWidth = getResources().getDisplayMetrics().widthPixels;
    int viewHeight = getResources().getDisplayMetrics().heightPixels;

    int width = bounds.right - bounds.left;
    int height = bounds.bottom - bounds.top;

    float minX = (-width + 20) * scaleX; 
    float minY = (-height + 20) * scaleY;

    if(transX > (viewWidth - 20)) {
        transX = viewWidth - 20;
    } else if(transX < minX) {
        transX = minX;
    }

    if(transY > (viewHeight - 80)) {
        transY = viewHeight - 80;
    } else if(transY < minY) {
        transY = minY;
    }

    values[Matrix.MTRANS_X] = transX;
    values[Matrix.MTRANS_Y] = transY; 
    m.setValues(values);
}

Encore une fois, cela devrait être placé juste après votre instruction switch et juste avant d'initialiser la matrice pour l'image dans la vue. J'ai également séparé la limitation du zoom dans une fonction et cela est reflété ci-dessus.

6voto

Téléchargez le code source. Ainsi, vous pouvez également obtenir ce zoom a) toucher, b) faire glisser et c) pincer. http://pragprog.com/titles/eband3/source_code

6voto

Farnad Tohidkhah Points 834

Voici le code complet pour le zoom et le panoramique par pincement (Touch.java avec quelques modifications qui peuvent être utilisées en pratique)

public class Touch implements OnTouchListener {  

 // Ces matrices seront utilisées pour déplacer et zoomer l'image  
public static Matrix matrix = new Matrix();  
public static Matrix savedMatrix = new Matrix();  

 // Nous pouvons être dans l'un de ces 3 états  
 static final int NONE = 0;  
 static final int DRAG = 1;  
 static final int ZOOM = 2;
private static final float MAX_ZOOM = (float) 3;
private static final float MIN_ZOOM = 1;  
 int mode = NONE;  

 // Se souvenir de certaines choses pour le zoom  
 PointF start = new PointF();  
 PointF mid = new PointF();  
 float oldDist = 1f;  

 int width,height;

 @Override  
 public boolean onTouch(View v, MotionEvent event) {

  ImageView view = (ImageView) v;
  Rect bounds = view.getDrawable().getBounds();

  width = bounds.right - bounds.left;
  height = bounds.bottom - bounds.top;
  // Output touch event to log  
  dumpEvent(event);  

  // Gérer les événements tactiles ici...  
  switch (event.getAction() & MotionEvent.ACTION_MASK) {  
  case MotionEvent.ACTION_DOWN:  
   savedMatrix.set(matrix);  
   start.set(event.getX(), event.getY());  
   mode = DRAG;  
   break;  
  case MotionEvent.ACTION_POINTER_DOWN:  
   oldDist = spacing(event);  
   if (oldDist > 10f) {  
    savedMatrix.set(matrix);  
    midPoint(mid, event);  
    mode = ZOOM;  
   }  
   break;  
  case MotionEvent.ACTION_UP:  
  case MotionEvent.ACTION_POINTER_UP:  
   mode = NONE;  
   break;  
  case MotionEvent.ACTION_MOVE:  
   if (mode == DRAG) {  
    // ...      
    matrix.set(savedMatrix);  
    matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);      
   } else if (mode == ZOOM) {  
    float newDist = spacing(event);  
    if (newDist > 10f) {  
     matrix.set(savedMatrix);  
     float scale = newDist / oldDist;  
     matrix.postScale(scale, scale, mid.x, mid.y);  
    }  
   }  
   break;  
  }  
//----------------------------------------------------
  limitZoom(matrix);
  limitDrag(matrix);
//----------------------------------------------------  
  view.setImageMatrix(matrix);  
  return true; // indiquer que l'événement a été géré  
 }  

 /** Afficher un événement dans la vue LogCat, pour le débogage */  
 private void dumpEvent(MotionEvent event) {  
  String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",  
    "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };  
  StringBuilder sb = new StringBuilder();  
  int action = event.getAction();  
  int actionCode = action & MotionEvent.ACTION_MASK;  
  sb.append("event ACTION_").append(names[actionCode]);  
  if (actionCode == MotionEvent.ACTION_POINTER_DOWN  
    || actionCode == MotionEvent.ACTION_POINTER_UP) {  
   sb.append("(pid ").append(  
     action >> MotionEvent.ACTION_POINTER_ID_SHIFT);  
   sb.append(")");  
  }  
  sb.append("[");  
  for (int i = 0; i < event.getPointerCount(); i++) {  
   sb.append("#").append(i);  
   sb.append("(pid ").append(event.getPointerId(i));  
   sb.append(")=").append((int) event.getX(i));  
   sb.append(",").append((int) event.getY(i));  
   if (i + 1 < event.getPointerCount())  
    sb.append(";");  
  }  
  sb.append("]");  
 }  

 /** Déterminer l'espace entre les deux premiers doigts */  
 private float spacing(MotionEvent event) {  
  float x = event.getX(0) - event.getX(1);  
  float y = event.getY(0) - event.getY(1);  
  return FloatMath.sqrt(x * x + y * y);  
 }  

 /** Calculer le point médian des deux premiers doigts */  
 private void midPoint(PointF point, MotionEvent event) {  
  float x = event.getX(0) + event.getX(1);  
  float y = event.getY(0) + event.getY(1);  
  point.set(x / 2, y / 2);  
 }  

 private void limitZoom(Matrix m) {

        float[] values = new float[9];
        m.getValues(values);
        float scaleX = values[Matrix.MSCALE_X];
        float scaleY = values[Matrix.MSCALE_Y];
        if(scaleX > MAX_ZOOM) {
            scaleX = MAX_ZOOM;
        } else if(scaleX < MIN_ZOOM) {
            scaleX = MIN_ZOOM;
        }

        if(scaleY > MAX_ZOOM) {
            scaleY = MAX_ZOOM;
        } else if(scaleY < MIN_ZOOM) {
            scaleY = MIN_ZOOM;
        }

        values[Matrix.MSCALE_X] = scaleX;
        values[Matrix.MSCALE_Y] = scaleY; 
        m.setValues(values);
    }

 private void limitDrag(Matrix m) {

        float[] values = new float[9];
        m.getValues(values);
        float transX = values[Matrix.MTRANS_X];
        float transY = values[Matrix.MTRANS_Y];
        float scaleX = values[Matrix.MSCALE_X];
        float scaleY = values[Matrix.MSCALE_Y];
//--- limiter le déplacement vers la gauche ---
        float minX = (-width + 0) * (scaleX-1); 
        float minY = (-height + 0) * (scaleY-1);
//--- limiter le déplacement vers la droite ---     
        float maxX=minX+width*(scaleX-1);
        float maxY=minY+height*(scaleY-1);
        if(transX>maxX){transX = maxX;}
        if(transXmaxY){transY = maxY;}
        if(transY

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X