207 votes

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 d'effectuer un zoom avant et arrière et de faire un panoramique de l'image?

Jusqu'à présent, j'ai trouvé deux façons:

  1. en écrasant ImageView, cela semble un peu trop pour un problème aussi commun.
  2. en utilisant un webview mais avec moins de contrôle sur la mise en page générale, etc.

209voto

Mike Ortiz Points 2649

Mise à JOUR

J'ai juste donné TouchImageView une nouvelle mise à jour. Il comprend maintenant Appuyez deux fois sur Zoom et les Jeter en plus de Panoramique et de Zoom par Pincement. Le code ci-dessous est très daté. Vous pouvez consulter le projet github pour obtenir la dernière version du code.

L'UTILISATION de la

Place TouchImageView.java dans votre projet. Il peut ensuite être utilisé de la même que ImageView. Exemple:

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

Si vous utilisez TouchImageView en xml, vous devez fournir l'ensemble complet nom, parce que c'est un affichage personnalisé. Exemple:

<com.example.touch.TouchImageView
    android:id="@+id/img"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Note: j'ai supprimé ma précédente réponse, dont certains très vieux code et maintenant relier directement à la plupart des mises à jour de code sur github.

ViewPager

Si vous êtes intéressé à mettre TouchImageView dans un ViewPager, reportez-vous à cette réponse.

80voto

Robert Foss Points 951

J'ai adapté un peu de code pour créer un TouchImageView qui supporte le multitouch (>2.1). Il est inspiré par le livre Bonjour, Android! (3e é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.Matrix;
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 String TAG = "Touch";
    // These matrices will be used to move and zoom image
    Matrix matrix = new Matrix();
    Matrix savedMatrix = new Matrix();

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

        matrix.setTranslate(1f, 1f);
        setImageMatrix(matrix);
        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:
                    savedMatrix.set(matrix);
                    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) {
                        savedMatrix.set(matrix);
                        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) {
                            matrix.set(savedMatrix);
                            float scale = newDist / oldDist;
                            matrix.postScale(scale, scale, mid.x, mid.y);
                        }
                    }
                    break;
                }

                setImageMatrix(matrix);
                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();
        }

        savedMatrix.set(matrix);
        matrix.set(savedMatrix);
        matrix.postScale(scale, scale, mid.x, mid.y);
        setImageMatrix(matrix);


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


        savedMatrix.set(matrix);
        matrix.set(savedMatrix);
        matrix.postTranslate(redundantXSpace, redundantYSpace);
        setImageMatrix(matrix);
    }


    /** Show an event in the LogCat view, for debugging */
    private void dumpEvent(WrapMotionEvent 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());
    }

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

60voto

Janusz Points 52607

J'ai utilisé un WebView et chargé l'image de la mémoire via

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

WebView gère tous les zooms et défilements de panoramique. Si vous utilisez wrap_content, la vue Web ne sera pas plus grande que l'image et aucune zone blanche ne sera affichée. Le WebView est le meilleur ImageView;)

7voto

digiphd Points 1316

En Réponse à Janusz question d'origine, il y a plusieurs façons de faire cela tous qui varient dans leur niveau de difficulté et ont été indiqués ci-dessous. À l'aide d'un affichage web est bon, mais il est très limité en termes de look et de contrôlabilité. Si vous dessinez une image à partir d'un canevas, le plus polyvalent des solutions qui ont été proposées semble être MikeOrtiz, Robert des logiciels libres et/ou Jacob Nordfalk suggéré. Il est un excellent exemple d'intégration de l'android-multitouch-contrôleur par PaulBourke, et est idéal pour avoir le support multi-touch et alltypes des vues personnalisées.

Personnellement, si vous êtes tout simplement en dessinant une toile à une image bitmap, puis de l'afficher à l'intérieur et à l'ImageView et que vous voulez être en mesure d'effectuer un zoom avant et se déplacer à l'aide de multi-touch, je trouve MikeOrtiz la solution la plus simple. Cependant, pour ma part le code de l' Git qu'il a fourni semble ne fonctionnent que lorsque son TouchImageView personnalisé ImageView de classe est le seul enfant ou de fournir la mise en page params:

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

Malheureusement, à cause de ma conception de mise en page, j'avais besoin de "wrap_content" pour "layout_height". Quand je l'ai changé à ce que l'image a été rognée en bas et je ne pouvais pas faire défiler ou de zoom de la zone isolée. Alors j'ai pris un coup d'oeil à la Source pour ImageView juste pour voir comment Android mis en œuvre "onMeasure" et changé MikeOrtiz de la fonction.

   @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);
    matrix.setScale(scale, scale);
    setImageMatrix(matrix);
    saveScale = 1f;

    // Center the image
    redundantYSpace = (float)height - (scale * (float)bmHeight) ;
    redundantXSpace = (float)width - (scale * (float)bmWidth);
    redundantYSpace /= (float)2;
    redundantXSpace /= (float)2;

    matrix.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);

    setImageMatrix(matrix);
}

Ici resolveSize(int,int) est un "Utilitaire de concilier une taille souhaitée avec les contraintes imposées par un MeasureSpec, où :

Paramètres:

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

Retourne:

 - The size this view should be."

Donc, essentiellement, offrant un comportement un peu plus semblable à l'original ImageView classe lorsque l'image est chargée. D'autres changements pourraient être apportés à l'appui d'une plus grande variété d'écrans qui modifient l'aspect ratio. Mais pour l'instant, j'Espère que cette aide. Grâce à MikeOrtiz pour son code d'origine, excellent travail.

6voto

Jacob Nordfalk Points 1505

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

La bibliothèque est vraiment géniale, bien qu'initialement un peu difficile à saisir.

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