Comment puis-je obtenir une fonctionnalité de zoom pour les images?

Existe-t-il un moyen courant d’afficher une grande image et de permettre à l’utilisateur de zoomer et dézoomer sur l’image?

Jusqu’à présent, j’ai trouvé deux moyens:

  1. écraser ImageView, cela semble un peu trop pour un problème si commun.
  2. utiliser une webview mais avec moins de contrôle sur la disposition générale, etc.

METTRE À JOUR

Je viens de donner à TouchImageView une nouvelle mise à jour. Il inclut désormais le zoom et le flirt double tap en plus du zoom panoramique et du pincement. Le code ci-dessous est très daté. Vous pouvez consulter le projet github pour obtenir le dernier code.

USAGE

Placez TouchImageView.java dans votre projet. Il peut alors être utilisé comme ImageView. Exemple:

TouchImageView img = (TouchImageView) findViewById(R.id.img); 

Si vous utilisez TouchImageView dans XML, vous devez fournir le nom complet du package, car il s’agit d’une vue personnalisée. Exemple:

  

Note: J’ai retiré ma réponse précédente, qui incluait un code très ancien et qui est maintenant lié directement au code le plus récent sur github.

ViewPager

Si vous souhaitez placer TouchImageView dans un ViewPager, reportez – vous à cette réponse.

J’ai adapté du code pour créer un TouchImageView prenant en charge le multitouch (> 2.1). Il est inspiré du livre Hello, Android! (3ème édition)

Il est contenu dans les 3 fichiers suivants TouchImageView.java WrapMotionEvent.java EclairMotionEvent.java

TouchImageView.java

 import se.robertfoss.ChanImageBrowser.Viewer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Masortingx; import android.graphics.PointF; import android.util.FloatMath; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; public class TouchImageView extends ImageView { private static final Ssortingng TAG = "Touch"; // These masortingces will be used to move and zoom image Masortingx masortingx = new Masortingx(); Masortingx savedMasortingx = new Masortingx(); // We can be in one of these 3 states static final int NONE = 0; static final int DRAG = 1; static final int ZOOM = 2; int mode = NONE; // Remember some things for zooming PointF start = new PointF(); PointF mid = new PointF(); float oldDist = 1f; Context context; public TouchImageView(Context context) { super(context); super.setClickable(true); this.context = context; masortingx.setTranslate(1f, 1f); setImageMasortingx(masortingx); setScaleType(ScaleType.MATRIX); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent rawEvent) { WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent); // Dump touch event to log if (Viewer.isDebug == true){ dumpEvent(event); } // Handle touch events here... switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: savedMasortingx.set(masortingx); start.set(event.getX(), event.getY()); Log.d(TAG, "mode=DRAG"); mode = DRAG; break; case MotionEvent.ACTION_POINTER_DOWN: oldDist = spacing(event); Log.d(TAG, "oldDist=" + oldDist); if (oldDist > 10f) { savedMasortingx.set(masortingx); midPoint(mid, event); mode = ZOOM; Log.d(TAG, "mode=ZOOM"); } break; case MotionEvent.ACTION_UP: int xDiff = (int) Math.abs(event.getX() - start.x); int yDiff = (int) Math.abs(event.getY() - start.y); if (xDiff < 8 && yDiff < 8){ performClick(); } case MotionEvent.ACTION_POINTER_UP: mode = NONE; Log.d(TAG, "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); Log.d(TAG, "newDist=" + newDist); if (newDist > 10f) { masortingx.set(savedMasortingx); float scale = newDist / oldDist; masortingx.postScale(scale, scale, mid.x, mid.y); } } break; } setImageMasortingx(masortingx); return true; // indicate event was handled } }); } public void setImage(Bitmap bm, int displayWidth, int displayHeight) { super.setImageBitmap(bm); //Fit to screen. float scale; if ((displayHeight / bm.getHeight()) >= (displayWidth / bm.getWidth())){ scale = (float)displayWidth / (float)bm.getWidth(); } else { scale = (float)displayHeight / (float)bm.getHeight(); } savedMasortingx.set(masortingx); masortingx.set(savedMasortingx); masortingx.postScale(scale, scale, mid.x, mid.y); setImageMasortingx(masortingx); // Center the image float redundantYSpace = (float)displayHeight - (scale * (float)bm.getHeight()) ; float redundantXSpace = (float)displayWidth - (scale * (float)bm.getWidth()); redundantYSpace /= (float)2; redundantXSpace /= (float)2; savedMasortingx.set(masortingx); masortingx.set(savedMasortingx); masortingx.postTranslate(redundantXSpace, redundantYSpace); setImageMasortingx(masortingx); } /** Show an event in the LogCat view, for debugging */ private void dumpEvent(WrapMotionEvent event) { // ... Ssortingng names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" }; SsortingngBuilder sb = new SsortingngBuilder(); 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()); } /** Determine the space between the first two fingers */ private float spacing(WrapMotionEvent event) { // ... float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return FloatMath.sqrt(x * x + y * y); } /** Calculate the mid point of the first two fingers */ private void midPoint(PointF point, WrapMotionEvent event) { // ... float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } } 

WrapMotionEvent.java

 import android.view.MotionEvent; public class WrapMotionEvent { protected MotionEvent event; protected WrapMotionEvent(MotionEvent event) { this.event = event; } static public WrapMotionEvent wrap(MotionEvent event) { try { return new EclairMotionEvent(event); } catch (VerifyError e) { return new WrapMotionEvent(event); } } public int getAction() { return event.getAction(); } public float getX() { return event.getX(); } public float getX(int pointerIndex) { verifyPointerIndex(pointerIndex); return getX(); } public float getY() { return event.getY(); } public float getY(int pointerIndex) { verifyPointerIndex(pointerIndex); return getY(); } public int getPointerCount() { return 1; } public int getPointerId(int pointerIndex) { verifyPointerIndex(pointerIndex); return 0; } private void verifyPointerIndex(int pointerIndex) { if (pointerIndex > 0) { throw new IllegalArgumentException( "Invalid pointer index for Donut/Cupcake"); } } } 

EclairMotionEvent.java

 import android.view.MotionEvent; public class EclairMotionEvent extends WrapMotionEvent { protected EclairMotionEvent(MotionEvent event) { super(event); } public float getX(int pointerIndex) { return event.getX(pointerIndex); } public float getY(int pointerIndex) { return event.getY(pointerIndex); } public int getPointerCount() { return event.getPointerCount(); } public int getPointerId(int pointerIndex) { return event.getPointerId(pointerIndex); } } 

J’ai utilisé une WebView et chargé l’image depuis la mémoire via

 webview.loadUrl("file://...") 

WebView gère tout le zoom panoramique et le défilement. Si vous utilisez wrap_content, la vue Web ne sera pas plus grande que l’image et aucune zone blanche ne sera affichée. WebView est le meilleur ImageView;)

En réponse à la question initiale de Janusz, il y a plusieurs manières d’y parvenir, toutes différentes dans leur niveau de difficulté et décrites ci-dessous. Utiliser une vue Web est une bonne chose, mais elle est très limitée en termes d’apparence et de contrôle. Si vous dessinez un bitmap à partir d’un canevas, les solutions les plus polyvalentes proposées semblent être celles de MikeOrtiz, Robert Foss et / ou ce que Jacob Nordfalk a suggéré. Il y a un excellent exemple d’incorporation du contrôleur multitouch android par PaulBourke , et il est idéal pour avoir le support multi-touch et tous les types de vues personnalisées.

Personnellement, si vous dessinez simplement un canevas sur un bitmap, puis affichez-le à l’intérieur et dans ImageView et que vous souhaitez pouvoir zoomer et déplacer en mode multi-touch, je trouve la solution de MikeOrtiz la plus simple. Cependant, le code de Git qu’il a fourni ne semble fonctionner que si sa classe personnalisée ImageView de TouchImageView est le seul enfant ou fournit les parameters de mise en page tels que:

 android:layout_height="match_parent" android:layout_height="match_parent" 

Malheureusement, en raison de mon schéma de configuration, j’avais besoin de “wrap_content” pour “layout_height”. Lorsque je l’ai changé pour cela, l’image a été recadrée en bas et je n’ai pas pu faire défiler ou zoomer sur la région recadrée. Donc, j’ai jeté un coup d’oeil à la source pour ImageView juste pour voir comment Android a implémenté “onMeasure” et a changé de MikeOrtiz pour convenir.

  @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //**** ADDED THIS ********///// int w = (int) bmWidth; int h = (int) bmHeight; width = resolveSize(w, widthMeasureSpec); height = resolveSize(h, heightMeasureSpec); //**** END ********/// // width = MeasureSpec.getSize(widthMeasureSpec); // REMOVED // height = MeasureSpec.getSize(heightMeasureSpec); // REMOVED //Fit to screen. float scale; float scaleX = (float)width / (float)bmWidth; float scaleY = (float)height / (float)bmHeight; scale = Math.min(scaleX, scaleY); masortingx.setScale(scale, scale); setImageMasortingx(masortingx); saveScale = 1f; // Center the image redundantYSpace = (float)height - (scale * (float)bmHeight) ; redundantXSpace = (float)width - (scale * (float)bmWidth); redundantYSpace /= (float)2; redundantXSpace /= (float)2; masortingx.postTranslate(redundantXSpace, redundantYSpace); origWidth = width - 2 * redundantXSpace; origHeight = height - 2 * redundantYSpace; // origHeight = bmHeight; right = width * saveScale - width - (2 * redundantXSpace * saveScale); bottom = height * saveScale - height - (2 * redundantYSpace * saveScale); setImageMasortingx(masortingx); } 

Ici resolveSize (int, int) est un “utilitaire permettant de concilier une taille souhaitée avec des contraintes imposées par un MeasureSpec, où:

Paramètres:

  - size How big the view wants to be - MeasureSpec Constraints imposed by the parent 

Résultats:

  - The size this view should be." 

Donc, essentiellement, fournir un comportement un peu plus similaire à la classe ImageView d’origine lorsque l’image est chargée. D’autres modifications pourraient être apscopes pour prendre en charge une plus grande variété d’écrans modifiant le rapport d’aspect. Mais pour l’instant, j’espère que cela aide. Merci à MikeOrtiz pour son code original, excellent travail.

Vous pouvez également essayer http://code.google.com/p/android-multitouch-controller/

La bibliothèque est vraiment géniale, même si au début, c’est un peu difficile à comprendre.

Je viens d’intégrer le TouchImageView de Robert Foss: il a parfaitement fonctionné! Merci!

Je viens de modifier un peu le code pour pouvoir l’instancier à partir de mon layout.xml.

Ajoutez simplement deux constructeurs

 public TouchImageView(Context context, AtsortingbuteSet attrs) { super(context, attrs); init(context); } public TouchImageView(Context context) { super(context); init(context); } 

et transformez l’ancien constructeur en une méthode init:

 private void init(Context context){ //...old code ofconstructor of Robert Moss's code } 

@Robert Foss, @Mike Ortiz, merci beaucoup pour votre travail. J’ai fusionné votre travail et terminé les cours de Robert pour Android> 2.0 avec Mike.

À la suite de mon travail, je présente Android Touch Gallery, basé sur ViewPager et utilisant TouchImageView modifié. Les images sont chargées par URL et vous pouvez les zoomer et les faire glisser. Vous pouvez le trouver ici https://github.com/Dreddik/AndroidTouchGallery

Essayez d’utiliser ZoomView pour zoomer sur une autre vue.

http://code.google.com/p/android-zoom-view/ c’est facile, gratuit et amusant à utiliser!

Ajout à la réponse de @ Mike. J’avais aussi besoin d’appuyer deux fois pour restaurer l’image aux dimensions d’origine lors de la première consultation. J’ai donc ajouté un tas de variables d’instance “orig …” et ajouté le SimpleOnGestureListener qui a fait l’affaire.

 import android.content.Context; import android.graphics.Bitmap; import android.graphics.Masortingx; import android.graphics.PointF; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; import android.widget.ImageView; public class TouchImageView extends ImageView { Masortingx masortingx = new Masortingx(); // We can be in one of these 3 states static final int NONE = 0; static final int DRAG = 1; static final int ZOOM = 2; int mode = NONE; // Remember some things for zooming PointF last = new PointF(); PointF start = new PointF(); float minScale = 1f; float maxScale = 3f; float[] m; float redundantXSpace, redundantYSpace, origRedundantXSpace, origRedundantYSpace;; float width, height; static final int CLICK = 3; static final float SAVE_SCALE = 1f; float saveScale = SAVE_SCALE; float right, bottom, origWidth, origHeight, bmWidth, bmHeight, origScale, origBottom,origRight; ScaleGestureDetector mScaleDetector; GestureDetector mGestureDetector; Context context; public TouchImageView(Context context) { super(context); super.setClickable(true); this.context = context; mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); masortingx.setTranslate(1f, 1f); m = new float[9]; setImageMasortingx(masortingx); setScaleType(ScaleType.MATRIX); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { boolean onDoubleTapEvent = mGestureDetector.onTouchEvent(event); if (onDoubleTapEvent) { // Reset Image to original scale values mode = NONE; bottom = origBottom; right = origRight; last = new PointF(); start = new PointF(); m = new float[9]; saveScale = SAVE_SCALE; masortingx = new Masortingx(); masortingx.setScale(origScale, origScale); masortingx.postTranslate(origRedundantXSpace, origRedundantYSpace); setImageMasortingx(masortingx); invalidate(); return true; } mScaleDetector.onTouchEvent(event); masortingx.getValues(m); float x = m[Masortingx.MTRANS_X]; float y = m[Masortingx.MTRANS_Y]; PointF curr = new PointF(event.getX(), event.getY()); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: last.set(event.getX(), event.getY()); start.set(last); mode = DRAG; break; case MotionEvent.ACTION_MOVE: if (mode == DRAG) { float deltaX = curr.x - last.x; float deltaY = curr.y - last.y; float scaleWidth = Math.round(origWidth * saveScale); float scaleHeight = Math.round(origHeight * saveScale); if (scaleWidth < width) { deltaX = 0; if (y + deltaY > 0) deltaY = -y; else if (y + deltaY < -bottom) deltaY = -(y + bottom); } else if (scaleHeight < height) { deltaY = 0; if (x + deltaX > 0) deltaX = -x; else if (x + deltaX < -right) deltaX = -(x + right); } else { if (x + deltaX > 0) deltaX = -x; else if (x + deltaX < -right) deltaX = -(x + right); if (y + deltaY > 0) deltaY = -y; else if (y + deltaY < -bottom) deltaY = -(y + bottom); } matrix.postTranslate(deltaX, deltaY); last.set(curr.x, curr.y); } break; case MotionEvent.ACTION_UP: mode = NONE; int xDiff = (int) Math.abs(curr.x - start.x); int yDiff = (int) Math.abs(curr.y - start.y); if (xDiff < CLICK && yDiff < CLICK) performClick(); break; case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; } setImageMatrix(matrix); invalidate(); return true; // indicate event was handled } }); mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTapEvent(MotionEvent e) { return true; } }); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); bmWidth = bm.getWidth(); bmHeight = bm.getHeight(); } public void setMaxZoom(float x) { maxScale = x; } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScaleBegin(ScaleGestureDetector detector) { mode = ZOOM; return true; } @Override public boolean onScale(ScaleGestureDetector detector) { float mScaleFactor = (float) Math.min( Math.max(.95f, detector.getScaleFactor()), 1.05); float origScale = saveScale; saveScale *= mScaleFactor; if (saveScale > maxScale) { saveScale = maxScale; mScaleFactor = maxScale / origScale; } else if (saveScale < minScale) { saveScale = minScale; mScaleFactor = minScale / origScale; } right = width * saveScale - width - (2 * redundantXSpace * saveScale); bottom = height * saveScale - height - (2 * redundantYSpace * saveScale); if (origWidth * saveScale <= width || origHeight * saveScale <= height) { matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2); if (mScaleFactor < 1) { matrix.getValues(m); float x = m[Matrix.MTRANS_X]; float y = m[Matrix.MTRANS_Y]; if (mScaleFactor < 1) { if (Math.round(origWidth * saveScale) < width) { if (y < -bottom) matrix.postTranslate(0, -(y + bottom)); else if (y > 0) masortingx.postTranslate(0, -y); } else { if (x < -right) matrix.postTranslate(-(x + right), 0); else if (x > 0) masortingx.postTranslate(-x, 0); } } } } else { masortingx.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY()); masortingx.getValues(m); float x = m[Masortingx.MTRANS_X]; float y = m[Masortingx.MTRANS_Y]; if (mScaleFactor < 1) { if (x < -right) matrix.postTranslate(-(x + right), 0); else if (x > 0) masortingx.postTranslate(-x, 0); if (y < -bottom) matrix.postTranslate(0, -(y + bottom)); else if (y > 0) masortingx.postTranslate(0, -y); } } return true; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); height = MeasureSpec.getSize(heightMeasureSpec); // Fit to screen. float scale; float scaleX = (float) width / (float) bmWidth; float scaleY = (float) height / (float) bmHeight; scale = Math.min(scaleX, scaleY); masortingx.setScale(scale, scale); setImageMasortingx(masortingx); saveScale = SAVE_SCALE; origScale = scale; // Center the image redundantYSpace = (float) height - (scale * (float) bmHeight); redundantXSpace = (float) width - (scale * (float) bmWidth); redundantYSpace /= (float) 2; redundantXSpace /= (float) 2; origRedundantXSpace = redundantXSpace; origRedundantYSpace = redundantYSpace; masortingx.postTranslate(redundantXSpace, redundantYSpace); origWidth = width - 2 * redundantXSpace; origHeight = height - 2 * redundantYSpace; right = width * saveScale - width - (2 * redundantXSpace * saveScale); bottom = height * saveScale - height - (2 * redundantYSpace * saveScale); origRight = right; origBottom = bottom; setImageMasortingx(masortingx); } } 

C’est un ajout très tardif à ce sujet, mais j’ai travaillé sur une vue d’image qui prend en charge le zoom et le panoramique et qui possède quelques fonctionnalités que je n’ai pas trouvées ailleurs. Cela a commencé comme un moyen d’afficher des images très volumineuses sans provoquer OutOfMemoryError s, en sous-échantillonnant l’image en zoom arrière et en chargeant des tuiles de plus haute résolution lorsqu’elle ViewPager un zoom. ), remplacer les événements tactiles sélectionnés à l’aide de OnClickListener ou de votre propre GestureDetector ou OnTouchListener , sous- OnTouchListener pour append des superpositions, effectuer un panoramique pendant le zoom et lancer un élan.

Il ne s’agit pas d’un remplacement à usage général, car ImageView ne l’étend pas et ne prend pas en charge l’affichage d’images provenant de ressources, uniquement des fichiers et des ressources externes. Il nécessite le SDK 10.

La source est sur GitHub, et il y a un exemple qui illustre l’utilisation dans un ViewPager .

https://github.com/davemorrissey/subsampling-scale-image-view

Vous pouvez essayer d’utiliser les LayoutParams pour cela

 public void zoom(boolean flag){ if(flag){ int width=40; int height=40; } else{ int width=20; int height=20; } RelativeLayout.LayoutParams param=new RelativeLayout.LayoutParams(width,height); //use the parent layout of the ImageView; imageView.setLayoutParams(param); //imageView is the view which needs zooming. } 

ZoomIn = zoom (true); ZoomOut = zoom (false);

 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageDetail = (ImageView) findViewById(R.id.imageView1); imageDetail.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { ImageView view = (ImageView) v; System.out.println("masortingx=" + savedMasortingx.toSsortingng()); switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: savedMasortingx.set(masortingx); startPoint.set(event.getX(), event.getY()); mode = DRAG; break; case MotionEvent.ACTION_POINTER_DOWN: oldDist = spacing(event); if (oldDist > 10f) { savedMasortingx.set(masortingx); midPoint(midPoint, event); mode = ZOOM; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: mode = NONE; break; case MotionEvent.ACTION_MOVE: if (mode == DRAG) { masortingx.set(savedMasortingx); masortingx.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y); } else if (mode == ZOOM) { float newDist = spacing(event); if (newDist > 10f) { masortingx.set(savedMasortingx); float scale = newDist / oldDist; masortingx.postScale(scale, scale, midPoint.x, midPoint.y); } } break; } view.setImageMasortingx(masortingx); return true; } @SuppressLint("FloatMath") 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); } }); } 

et le dossier pouvant être dessiné devrait avoir un fichier image bticn. fonctionne parfaitement 🙂

Quelque chose comme ci-dessous le fera.

 @Override public boolean onTouch(View v,MotionEvent e) { tap=tap2=drag=pinch=none; int mask=e.getActionMasked(); posx=e.getX();posy=e.getY(); float midx= img.getWidth()/2f; float midy=img.getHeight()/2f; int fingers=e.getPointerCount(); switch(mask) { case MotionEvent.ACTION_POINTER_UP: tap2=1;break; case MotionEvent.ACTION_UP: tap=1;break; case MotionEvent.ACTION_MOVE: drag=1; } if(fingers==2){nowsp=Math.abs(e.getX(0)-e.getX(1));} if((fingers==2)&&(drag==0)){ tap2=1;tap=0;drag=0;} if((fingers==2)&&(drag==1)){ tap2=0;tap=0;drag=0;pinch=1;} if(pinch==1) { if(nowsp>oldsp)scale+=0.1; if(nowsp 

Pour voir le programme complet, cliquez ici: Programmez pour zoomer l'image dans Android