双击缩放和缩放以放大Android中的ImageView

问题描述 投票:7回答:4

我正在寻找iphone,如使用Android ImageView轻拍和捏缩放。 Mike Ortiz在TouchImageView上做了一些很好的工作来检测边界。他的代码可以找到here

此代码只缺少一件事,即双击缩放。任何人都可以帮助将此功能添加到Mike Ortiz代码中吗?

android imageview zoom pinch
4个回答
6
投票

Pinch放大Android中的ImageView并使用方向处理

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.SimpleOnGestureListener;

public class ZoomableImageView extends View {
    private static final String TAG = "ZoomableImageView";       

    private Bitmap imgBitmap = null;

    private int containerWidth;
    private int containerHeight;

    Paint background;   

    //Matrices will be used to move and zoom image
    Matrix matrix = new Matrix();
    Matrix savedMatrix = new Matrix();

    PointF start = new PointF();       

    float currentScale;
    float curX;
    float curY;

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

    //For animating stuff   
    float targetX;
    float targetY;
    float targetScale;
    float targetScaleX;
    float targetScaleY;
    float scaleChange;
    float targetRatio;
    float transitionalRatio;

    float easing = 0.2f;   
    boolean isAnimating = false;

    float scaleDampingFactor = 0.5f;

    //For pinch and zoom
    float oldDist = 1f;   
    PointF mid = new PointF();

    private Handler mHandler = new Handler();       

    float minScale;
    float maxScale = 8.0f;

    float wpRadius = 25.0f;
    float wpInnerRadius = 20.0f;

    float screenDensity;

    private GestureDetector gestureDetector;

    public static final int DEFAULT_SCALE_FIT_INSIDE = 0;
    public static final int DEFAULT_SCALE_ORIGINAL = 1;

    private int defaultScale;

    public int getDefaultScale() {
        return defaultScale;
    }

    public void setDefaultScale(int defaultScale) {
        this.defaultScale = defaultScale;
    }

    public ZoomableImageView(Context context) {
        super(context);       
        setFocusable(true);
        setFocusableInTouchMode(true);

        screenDensity = context.getResources().getDisplayMetrics().density;

        initPaints();
        gestureDetector = new GestureDetector(new MyGestureDetector());       
    }

    public ZoomableImageView(Context context, AttributeSet attrs) {
        super(context, attrs);

        screenDensity = context.getResources().getDisplayMetrics().density;       
        initPaints();
        gestureDetector = new GestureDetector(new MyGestureDetector());

        defaultScale = ZoomableImageView.DEFAULT_SCALE_FIT_INSIDE;
    }

    private void initPaints() {
        background = new Paint();
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
        super.onSizeChanged(width, height, oldWidth, oldHeight);

        //Reset the width and height. Will draw bitmap and change
        containerWidth = width;
        containerHeight = height;

        if(imgBitmap != null) {
            int imgHeight = imgBitmap.getHeight();
            int imgWidth = imgBitmap.getWidth();

            float scale;
            int initX = 0;
            int initY = 0;           

            if(defaultScale == ZoomableImageView.DEFAULT_SCALE_FIT_INSIDE) {               
                if(imgWidth > containerWidth) {           
                    scale = (float)containerWidth / imgWidth;           
                    float newHeight = imgHeight * scale;           
                    initY = (containerHeight - (int)newHeight)/2;

                    matrix.setScale(scale, scale);
                    matrix.postTranslate(0, initY);
                }
                else {           
                    scale = (float)containerHeight / imgHeight;
                    float newWidth = imgWidth * scale;
                    initX = (containerWidth - (int)newWidth)/2;

                    matrix.setScale(scale, scale);
                    matrix.postTranslate(initX, 0);
                }

                curX = initX;
                curY = initY;

                currentScale = scale;
                minScale = scale;
            }
            else {
                if(imgWidth > containerWidth) {                                   
                    initY = (containerHeight - (int)imgHeight)/2;                   
                    matrix.postTranslate(0, initY);
                }
                else {                               
                    initX = (containerWidth - (int)imgWidth)/2;                   
                    matrix.postTranslate(initX, 0);
                }

                curX = initX;
                curY = initY;

                currentScale = 1.0f;
                minScale = 1.0f;               
            }


            invalidate();           
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {               
        if(imgBitmap != null && canvas != null)
        {                                           
            canvas.drawBitmap(imgBitmap, matrix, background);                                                   
        }
    }

    //Checks and sets the target image x and y co-ordinates if out of bounds
    private void checkImageConstraints() {
        if(imgBitmap == null) {
            return;
        }

        float[] mvals = new float[9];
        matrix.getValues(mvals);

        currentScale = mvals[0];

        if(currentScale < minScale) {                               
            float deltaScale = minScale / currentScale;                   
            float px = containerWidth/2;
            float py = containerHeight/2;           
            matrix.postScale(deltaScale, deltaScale, px, py);
            invalidate();
        }       

        matrix.getValues(mvals);
        currentScale = mvals[0];
        curX = mvals[2];
        curY = mvals[5];

        int rangeLimitX = containerWidth - (int)(imgBitmap.getWidth() * currentScale);
        int rangeLimitY = containerHeight - (int)(imgBitmap.getHeight() * currentScale);


        boolean toMoveX = false;
        boolean toMoveY = false;   

        if(rangeLimitX < 0) {
            if(curX > 0) {
                targetX = 0;
                toMoveX = true;
            }
            else if(curX < rangeLimitX) {
                targetX = rangeLimitX;
                toMoveX = true;
            }
        }
        else {
            targetX = rangeLimitX / 2;
            toMoveX = true;
        }

        if(rangeLimitY < 0) {
            if(curY > 0) {
                targetY = 0;
                toMoveY = true;
            }
            else if(curY < rangeLimitY) {
                targetY = rangeLimitY;
                toMoveY = true;
            }
        }
        else {
            targetY = rangeLimitY / 2;
            toMoveY = true;
        }

        if(toMoveX == true || toMoveY == true) {
            if(toMoveY == false) {
                targetY = curY;
            }
            if(toMoveX == false) {
                targetX = curX;
            }           

            //Disable touch event actions
            isAnimating = true;
            //Initialize timer           
            mHandler.removeCallbacks(mUpdateImagePositionTask);
            mHandler.postDelayed(mUpdateImagePositionTask, 100);
        }
    }       


    @Override
    public boolean onTouchEvent(MotionEvent event) {       
        if(gestureDetector.onTouchEvent(event)) {
            return true;
        }

        if(isAnimating == true) {
            return true;
        }

        //Handle touch events here       
        float[] mvals = new float[9];
        switch(event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            if(isAnimating == false) {
                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;

            matrix.getValues(mvals);
            curX = mvals[2];
            curY = mvals[5];
            currentScale = mvals[0];

            if(isAnimating == false) {                                       
                checkImageConstraints();
            }
        break;

        case MotionEvent.ACTION_MOVE:           
            if(mode == DRAG && isAnimating == false) {
                matrix.set(savedMatrix);
                float diffX = event.getX() - start.x;
                float diffY = event.getY() - start.y;

                matrix.postTranslate(diffX, diffY);

                matrix.getValues(mvals);
                curX = mvals[2];
                curY = mvals[5];
                currentScale = mvals[0];
            }
            else if(mode == ZOOM && isAnimating == false) {
                float newDist = spacing(event);               
                if(newDist > 10f) {
                    matrix.set(savedMatrix);
                    float scale = newDist / oldDist;                   
                    matrix.getValues(mvals);
                    currentScale = mvals[0];

                    if(currentScale * scale <= minScale) {
                        matrix.postScale(minScale/currentScale, minScale/currentScale, mid.x, mid.y);
                    }                   
                    else if(currentScale * scale >= maxScale) {
                        matrix.postScale(maxScale/currentScale, maxScale/currentScale, mid.x, mid.y);
                    }
                    else {
                        matrix.postScale(scale, scale, mid.x, mid.y);
                    }


                    matrix.getValues(mvals);
                    curX = mvals[2];
                    curY = mvals[5];
                    currentScale = mvals[0];                                       
                }
            }

        break;                               
        }

        //Calculate the transformations and then invalidate
        invalidate();
        return true;
    }

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

    public void setImageBitmap(Bitmap b) {       
        if(b != null) {
            imgBitmap = b;               

            containerWidth = getWidth();
            containerHeight = getHeight();

            int imgHeight = imgBitmap.getHeight();
            int imgWidth = imgBitmap.getWidth();

            float scale;
            int initX = 0;
            int initY = 0;

            matrix.reset();

            if(defaultScale == ZoomableImageView.DEFAULT_SCALE_FIT_INSIDE) {               
                if(imgWidth > containerWidth) {           
                    scale = (float)containerWidth / imgWidth;           
                    float newHeight = imgHeight * scale;           
                    initY = (containerHeight - (int)newHeight)/2;

                    matrix.setScale(scale, scale);
                    matrix.postTranslate(0, initY);
                }
                else {           
                    scale = (float)containerHeight / imgHeight;
                    float newWidth = imgWidth * scale;
                    initX = (containerWidth - (int)newWidth)/2;

                    matrix.setScale(scale, scale);
                    matrix.postTranslate(initX, 0);
                }

                curX = initX;
                curY = initY;

                currentScale = scale;
                minScale = scale;
            }
            else {
                if(imgWidth > containerWidth) {
                    initX = 0;
                    if(imgHeight > containerHeight) {                       
                        initY = 0;
                    }
                    else {                       
                        initY = (containerHeight - (int)imgHeight)/2;
                    }

                    matrix.postTranslate(0, initY);
                }
                else {                               
                    initX = (containerWidth - (int)imgWidth)/2;
                    if(imgHeight > containerHeight) {
                        initY = 0;
                    }
                    else {
                        initY = (containerHeight - (int)imgHeight)/2;
                    }
                    matrix.postTranslate(initX, 0);
                }

                curX = initX;
                curY = initY;

                currentScale = 1.0f;
                minScale = 1.0f;               
            }

            invalidate();           
        }
        else {
            Log.d(TAG, "bitmap is null");
        }
    }

    public Bitmap getPhotoBitmap() {       
        return imgBitmap;
    }


    private Runnable mUpdateImagePositionTask = new Runnable() {
        public void run() {       
            float[] mvals;

            if(Math.abs(targetX - curX) < 5 && Math.abs(targetY - curY) < 5) {
                isAnimating = false;
                mHandler.removeCallbacks(mUpdateImagePositionTask);

                mvals = new float[9];
                matrix.getValues(mvals);

                currentScale = mvals[0];
                curX = mvals[2];
                curY = mvals[5];

                //Set the image parameters and invalidate display
                float diffX = (targetX - curX);
                float diffY = (targetY - curY);

                matrix.postTranslate(diffX, diffY);
            }
            else {
                isAnimating = true;
                mvals = new float[9];
                matrix.getValues(mvals);

                currentScale = mvals[0];
                curX = mvals[2];
                curY = mvals[5];

                //Set the image parameters and invalidate display
                float diffX = (targetX - curX) * 0.3f;
                float diffY = (targetY - curY) * 0.3f;

                matrix.postTranslate(diffX, diffY);               
                mHandler.postDelayed(this, 25);               
            }

            invalidate();           
        }
    };

    private Runnable mUpdateImageScale = new Runnable() {
        public void run() {           
            float transitionalRatio = targetScale / currentScale;           
            float dx;
            if(Math.abs(transitionalRatio - 1) > 0.05) {
                isAnimating = true;               
                if(targetScale > currentScale) {                                       
                    dx = transitionalRatio - 1;
                    scaleChange = 1 + dx * 0.2f;

                    currentScale *= scaleChange;

                    if(currentScale > targetScale) {
                        currentScale = currentScale / scaleChange;
                        scaleChange = 1;
                    }
                }
                else {                                   
                    dx = 1 - transitionalRatio;                   
                    scaleChange = 1 - dx * 0.5f;
                    currentScale *= scaleChange;

                    if(currentScale < targetScale) {
                        currentScale = currentScale / scaleChange;
                        scaleChange = 1;
                    }
                }


                if(scaleChange != 1) {
                    matrix.postScale(scaleChange, scaleChange, targetScaleX, targetScaleY);               
                    mHandler.postDelayed(mUpdateImageScale, 15);
                    invalidate();
                }
                else {
                    isAnimating = false;
                    scaleChange = 1;                   
                    matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY);
                    currentScale = targetScale;
                    mHandler.removeCallbacks(mUpdateImageScale);
                    invalidate();
                    checkImageConstraints();
                }               
            }
            else {
                isAnimating = false;
                scaleChange = 1;               
                matrix.postScale(targetScale/currentScale, targetScale/currentScale, targetScaleX, targetScaleY);
                currentScale = targetScale;
                mHandler.removeCallbacks(mUpdateImageScale);
                invalidate();
                checkImageConstraints();
            }                               
        }
    };

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

   class MyGestureDetector extends SimpleOnGestureListener {
        @Override
        public boolean onDoubleTap(MotionEvent event) {           
            if(isAnimating == true) {
                return true;
            }

            scaleChange = 1;
            isAnimating = true;
            targetScaleX = event.getX();
            targetScaleY = event.getY();

            if(Math.abs(currentScale - maxScale) > 0.1) {           
                targetScale = maxScale;
            }
            else {
                targetScale = minScale;
            }
            targetRatio = targetScale / currentScale;
            mHandler.removeCallbacks(mUpdateImageScale);
            mHandler.post(mUpdateImageScale);           
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            return super.onFling(e1, e2, velocityX, velocityY);
        }

        @Override
        public boolean onDown(MotionEvent e) {
            return false;
        }
    }
}

2
投票

它已经过时了,但由于没有选择正确答案,我想分享一下。

我已经调整了this中的TouchImageView类,通过实现GestureDetector.OnGestureListenerGestureDetector.OnDoubleTapListener接口来支持双击放大/缩小

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
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 implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {

    Matrix 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 last = new PointF();
    PointF start = new PointF();
    float minScale = 1f;
    float maxScale = 3f;
    float[] m;

    int viewWidth, viewHeight;
    static final int CLICK = 3;
    float saveScale = 1f;
    protected float origWidth, origHeight;
    int oldMeasuredWidth, oldMeasuredHeight;

    ScaleGestureDetector mScaleDetector;

    Context context;

    public TouchImageView(Context context) {
        super(context);
        sharedConstructing(context);
    }

    public TouchImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        sharedConstructing(context);
    }

    GestureDetector mGestureDetector;

    private void sharedConstructing(Context context) {
        super.setClickable(true);
        this.context = context;
        mGestureDetector = new GestureDetector(context, this);
        mGestureDetector.setOnDoubleTapListener(this);

        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        matrix = new Matrix();
        m = new float[9];
        setImageMatrix(matrix);
        setScaleType(ScaleType.MATRIX);

        setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                mScaleDetector.onTouchEvent(event);
                mGestureDetector.onTouchEvent(event);

                PointF curr = new PointF(event.getX(), event.getY());

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        last.set(curr);
                        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 fixTransX = getFixDragTrans(deltaX, viewWidth,
                                    origWidth * saveScale);
                            float fixTransY = getFixDragTrans(deltaY, viewHeight,
                                    origHeight * saveScale);
                            matrix.postTranslate(fixTransX, fixTransY);
                            fixTrans();
                            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
            }

        });
    }

    public void setMaxZoom(float x) {
        maxScale = x;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        // Double tap is detected
        Log.i("MAIN_TAG", "Double tap detected");
        float origScale = saveScale;
        float mScaleFactor;

        if (saveScale == maxScale) {
            saveScale = minScale;
            mScaleFactor = minScale / origScale;
        } else {
            saveScale = maxScale;
            mScaleFactor = maxScale / origScale;
        }

        matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2,
                viewHeight / 2);

        fixTrans();
        return false;
    }

    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }

    private class ScaleListener extends
            ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            mode = ZOOM;
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float mScaleFactor = detector.getScaleFactor();
            float origScale = saveScale;
            saveScale *= mScaleFactor;
            if (saveScale > maxScale) {
                saveScale = maxScale;
                mScaleFactor = maxScale / origScale;
            } else if (saveScale < minScale) {
                saveScale = minScale;
                mScaleFactor = minScale / origScale;
            }

            if (origWidth * saveScale <= viewWidth
                    || origHeight * saveScale <= viewHeight)
                matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2,
                        viewHeight / 2);
            else
                matrix.postScale(mScaleFactor, mScaleFactor,
                        detector.getFocusX(), detector.getFocusY());

            fixTrans();
            return true;
        }
    }

    void fixTrans() {
        matrix.getValues(m);
        float transX = m[Matrix.MTRANS_X];
        float transY = m[Matrix.MTRANS_Y];

        float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
        float fixTransY = getFixTrans(transY, viewHeight, origHeight
                * saveScale);

        if (fixTransX != 0 || fixTransY != 0)
            matrix.postTranslate(fixTransX, fixTransY);
    }

    float getFixTrans(float trans, float viewSize, float contentSize) {
        float minTrans, maxTrans;

        if (contentSize <= viewSize) {
            minTrans = 0;
            maxTrans = viewSize - contentSize;
        } else {
            minTrans = viewSize - contentSize;
            maxTrans = 0;
        }

        if (trans < minTrans)
            return -trans + minTrans;
        if (trans > maxTrans)
            return -trans + maxTrans;
        return 0;
    }

    float getFixDragTrans(float delta, float viewSize, float contentSize) {
        if (contentSize <= viewSize) {
            return 0;
        }
        return delta;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewWidth = MeasureSpec.getSize(widthMeasureSpec);
        viewHeight = MeasureSpec.getSize(heightMeasureSpec);

        //
        // Rescales image on rotation
        //
        if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
                || viewWidth == 0 || viewHeight == 0)
            return;
        oldMeasuredHeight = viewHeight;
        oldMeasuredWidth = viewWidth;

        if (saveScale == 1) {
            // Fit to screen.
            float scale;

            Drawable drawable = getDrawable();
            if (drawable == null || drawable.getIntrinsicWidth() == 0
                    || drawable.getIntrinsicHeight() == 0)
                return;
            int bmWidth = drawable.getIntrinsicWidth();
            int bmHeight = drawable.getIntrinsicHeight();

            Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);

            float scaleX = (float) viewWidth / (float) bmWidth;
            float scaleY = (float) viewHeight / (float) bmHeight;
            scale = Math.min(scaleX, scaleY);
            matrix.setScale(scale, scale);

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

            matrix.postTranslate(redundantXSpace, redundantYSpace);

            origWidth = viewWidth - 2 * redundantXSpace;
            origHeight = viewHeight - 2 * redundantYSpace;
            setImageMatrix(matrix);
        }
        fixTrans();
    }
}

用法:你可以用XML和java中的TouchImageView替换你的qazxsw poi

1.转发CML

ImageView

2.对于Java

<?xml version="1.0" encoding="utf-8"?>
<com.example.android.myapp.TouchImageView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/imViewedImage"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true"
    android:focusable="true" />

0
投票

使用singletap监听器的Zoomable imageview

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

使用此方法进行单击

import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import it.sephiroth.android.library.imagezoom.ImageViewTouch;

public class MyZoomableImageViewTouch extends ImageViewTouch
{

    static final float SCROLL_DELTA_THRESHOLD = 1.0f;

    public MyZoomableImageViewTouch(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        init();
    }

    public MyZoomableImageViewTouch(Context context, AttributeSet attrs) {
         super(context, attrs);
         init();
    }

    public MyZoomableImageViewTouch(Context context)
    {
        super(context);
        init();
    }

    private void init() {
        View.OnTouchListener listener = new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                 if (getScale() > 1f) {
                 getParent().requestDisallowInterceptTouchEvent(true);
                 } else {
                 getParent().requestDisallowInterceptTouchEvent(false);
                 }
                 return false;
            }
           };
           setOnTouchListener(listener);
           setDisplayType(DisplayType.FIT_TO_SCREEN);
     }

     @Override
    protected float onDoubleTapPost(float scale, float maxZoom) {
        if (scale != 1f) {
           mDoubleTapDirection = 1;
           return 1f;
       }
       if (mDoubleTapDirection == 1) {
            mDoubleTapDirection = -1;
          if ((scale + (mScaleFactor * 2)) <= maxZoom) {
              return scale + mScaleFactor;
          }   else {
              mDoubleTapDirection = -1;
              return maxZoom;
          }
        } else {
          mDoubleTapDirection = 1;
          return 1f;
       }
  }

    @Override
    public boolean canScroll(int direction) {
       RectF bitmapRect = getBitmapRect();
       updateRect(bitmapRect, mScrollRect);
       Rect imageViewRect = new Rect();
       getGlobalVisibleRect(imageViewRect);

       if (null == bitmapRect) {
          return false;
       }

       if (Math.abs(bitmapRect.right - imageViewRect.right) < SCROLL_DELTA_THRESHOLD) {
           if (direction < 0) {
                return false;
           }
       }

       if (Math.abs(bitmapRect.left - mScrollRect.left) <  SCROLL_DELTA_THRESHOLD) {
           if (direction > 0) {
               return false;
           }
       }
       return true;
   }

   @Override
   public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
   {
        if (getScale() == 1f) return false;
        if (distanceX != 0 && !canScroll((int) -distanceX)) {
             getParent().requestDisallowInterceptTouchEvent(false);
             return false;
        } else {
             getParent().requestDisallowInterceptTouchEvent(true);
             mUserScaled = true;
             scrollBy(-distanceX, -distanceY);
            invalidate();
            return true;
       }
   }
}

0
投票

这里它适用于双指缩放和双击缩放:

    imageView.setSingleTapListener(new ImageViewTouch.OnImageViewTouchSingleTapListener()
    {
        @Override
        public void onSingleTapConfirmed()
        {
        }
     }

参考:import android.annotation.TargetApi; import android.content.Context; import android.content.res.Configuration; import android.graphics.*; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.OverScroller; import android.widget.Scroller; import androidx.appcompat.widget.AppCompatImageView; public class TouchImageView extends AppCompatImageView { private static final String DEBUG = "DEBUG"; // // SuperMin and SuperMax multipliers. Determine how much the image can be // zoomed below or above the zoom boundaries, before animating back to the // min/max zoom boundary. // private static final float SUPER_MIN_MULTIPLIER = .75f; private static final float SUPER_MAX_MULTIPLIER = 1.25f; // // Scale of image ranges from minScale to maxScale, where minScale == 1 // when the image is stretched to fit view. // private float normalizedScale; // // Matrix applied to image. MSCALE_X and MSCALE_Y should always be equal. // MTRANS_X and MTRANS_Y are the other values used. prevMatrix is the matrix // saved prior to the screen rotating. // private Matrix matrix, prevMatrix; private static enum State {NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM} ; private State state; private float minScale; private float maxScale; private float superMinScale; private float superMaxScale; private float[] m; private Context context; private Fling fling; private ScaleType mScaleType; private boolean imageRenderedAtLeastOnce; private boolean onDrawReady; private ZoomVariables delayedZoomVariables; // // Size of view and previous view size (ie before rotation) // private int viewWidth, viewHeight, prevViewWidth, prevViewHeight; // // Size of image when it is stretched to fit view. Before and After rotation. // private float matchViewWidth, matchViewHeight, prevMatchViewWidth, prevMatchViewHeight; private ScaleGestureDetector mScaleDetector; private GestureDetector mGestureDetector; private GestureDetector.OnDoubleTapListener doubleTapListener = null; private OnTouchListener userTouchListener = null; private OnTouchImageViewListener touchImageViewListener = null; public TouchImageView(Context context) { super(context); sharedConstructing(context); } public TouchImageView(Context context, AttributeSet attrs) { super(context, attrs); sharedConstructing(context); } public TouchImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); sharedConstructing(context); } private void sharedConstructing(Context context) { super.setClickable(true); this.context = context; mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); mGestureDetector = new GestureDetector(context, new GestureListener()); matrix = new Matrix(); prevMatrix = new Matrix(); m = new float[9]; normalizedScale = 1; if (mScaleType == null) { mScaleType = ScaleType.FIT_CENTER; } minScale = 1; maxScale = 3; superMinScale = SUPER_MIN_MULTIPLIER * minScale; superMaxScale = SUPER_MAX_MULTIPLIER * maxScale; setImageMatrix(matrix); setScaleType(ScaleType.MATRIX); setState(State.NONE); onDrawReady = false; super.setOnTouchListener(new PrivateOnTouchListener()); } @Override public void setOnTouchListener(OnTouchListener l) { userTouchListener = l; } public void setOnTouchImageViewListener(OnTouchImageViewListener l) { touchImageViewListener = l; } public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener l) { doubleTapListener = l; } @Override public void setImageResource(int resId) { super.setImageResource(resId); savePreviousImageValues(); fitImageToView(); } @Override public void setImageBitmap(Bitmap bm) { super.setImageBitmap(bm); savePreviousImageValues(); fitImageToView(); } @Override public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); savePreviousImageValues(); fitImageToView(); } @Override public void setImageURI(Uri uri) { super.setImageURI(uri); savePreviousImageValues(); fitImageToView(); } @Override public void setScaleType(ScaleType type) { if (type == ScaleType.FIT_START || type == ScaleType.FIT_END) { throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END"); } if (type == ScaleType.MATRIX) { super.setScaleType(ScaleType.MATRIX); } else { mScaleType = type; if (onDrawReady) { // // If the image is already rendered, scaleType has been called programmatically // and the TouchImageView should be updated with the new scaleType. // setZoom(this); } } } @Override public ScaleType getScaleType() { return mScaleType; } /** * Returns false if image is in initial, unzoomed state. False, otherwise. * * @return true if image is zoomed */ public boolean isZoomed() { return normalizedScale != 1; } /** * Return a Rect representing the zoomed image. * * @return rect representing zoomed image */ public RectF getZoomedRect() { if (mScaleType == ScaleType.FIT_XY) { throw new UnsupportedOperationException("getZoomedRect() not supported with FIT_XY"); } PointF topLeft = transformCoordTouchToBitmap(0, 0, true); PointF bottomRight = transformCoordTouchToBitmap(viewWidth, viewHeight, true); float w = getDrawable().getIntrinsicWidth(); float h = getDrawable().getIntrinsicHeight(); return new RectF(topLeft.x / w, topLeft.y / h, bottomRight.x / w, bottomRight.y / h); } /** * Save the current matrix and view dimensions * in the prevMatrix and prevView variables. */ private void savePreviousImageValues() { if (matrix != null && viewHeight != 0 && viewWidth != 0) { matrix.getValues(m); prevMatrix.setValues(m); prevMatchViewHeight = matchViewHeight; prevMatchViewWidth = matchViewWidth; prevViewHeight = viewHeight; prevViewWidth = viewWidth; } } @Override public Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable("instanceState", super.onSaveInstanceState()); bundle.putFloat("saveScale", normalizedScale); bundle.putFloat("matchViewHeight", matchViewHeight); bundle.putFloat("matchViewWidth", matchViewWidth); bundle.putInt("viewWidth", viewWidth); bundle.putInt("viewHeight", viewHeight); matrix.getValues(m); bundle.putFloatArray("matrix", m); bundle.putBoolean("imageRendered", imageRenderedAtLeastOnce); return bundle; } @Override public void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; normalizedScale = bundle.getFloat("saveScale"); m = bundle.getFloatArray("matrix"); prevMatrix.setValues(m); prevMatchViewHeight = bundle.getFloat("matchViewHeight"); prevMatchViewWidth = bundle.getFloat("matchViewWidth"); prevViewHeight = bundle.getInt("viewHeight"); prevViewWidth = bundle.getInt("viewWidth"); imageRenderedAtLeastOnce = bundle.getBoolean("imageRendered"); super.onRestoreInstanceState(bundle.getParcelable("instanceState")); return; } super.onRestoreInstanceState(state); } @Override protected void onDraw(Canvas canvas) { onDrawReady = true; imageRenderedAtLeastOnce = true; if (delayedZoomVariables != null) { setZoom(delayedZoomVariables.scale, delayedZoomVariables.focusX, delayedZoomVariables.focusY, delayedZoomVariables.scaleType); delayedZoomVariables = null; } super.onDraw(canvas); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); savePreviousImageValues(); } /** * Get the max zoom multiplier. * * @return max zoom multiplier. */ public float getMaxZoom() { return maxScale; } /** * Set the max zoom multiplier. Default value: 3. * * @param max max zoom multiplier. */ public void setMaxZoom(float max) { maxScale = max; superMaxScale = SUPER_MAX_MULTIPLIER * maxScale; } /** * Get the min zoom multiplier. * * @return min zoom multiplier. */ public float getMinZoom() { return minScale; } /** * Get the current zoom. This is the zoom relative to the initial * scale, not the original resource. * * @return current zoom multiplier. */ public float getCurrentZoom() { return normalizedScale; } /** * Set the min zoom multiplier. Default value: 1. * * @param min min zoom multiplier. */ public void setMinZoom(float min) { minScale = min; superMinScale = SUPER_MIN_MULTIPLIER * minScale; } /** * Reset zoom and translation to initial state. */ public void resetZoom() { normalizedScale = 1; fitImageToView(); } /** * Set zoom to the specified scale. Image will be centered by default. * * @param scale */ public void setZoom(float scale) { setZoom(scale, 0.5f, 0.5f); } /** * Set zoom to the specified scale. Image will be centered around the point * (focusX, focusY). These floats range from 0 to 1 and denote the focus point * as a fraction from the left and top of the view. For example, the top left * corner of the image would be (0, 0). And the bottom right corner would be (1, 1). * * @param scale * @param focusX * @param focusY */ public void setZoom(float scale, float focusX, float focusY) { setZoom(scale, focusX, focusY, mScaleType); } /** * Set zoom to the specified scale. Image will be centered around the point * (focusX, focusY). These floats range from 0 to 1 and denote the focus point * as a fraction from the left and top of the view. For example, the top left * corner of the image would be (0, 0). And the bottom right corner would be (1, 1). * * @param scale * @param focusX * @param focusY * @param scaleType */ public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) { // // setZoom can be called before the image is on the screen, but at this point, // image and view sizes have not yet been calculated in onMeasure. Thus, we should // delay calling setZoom until the view has been measured. // if (!onDrawReady) { delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType); return; } if (scaleType != mScaleType) { setScaleType(scaleType); } resetZoom(); scaleImage(scale, viewWidth / 2, viewHeight / 2, true); matrix.getValues(m); m[Matrix.MTRANS_X] = -((focusX * getImageWidth()) - (viewWidth * 0.5f)); m[Matrix.MTRANS_Y] = -((focusY * getImageHeight()) - (viewHeight * 0.5f)); matrix.setValues(m); fixTrans(); setImageMatrix(matrix); } /** * Set zoom parameters equal to another TouchImageView. Including scale, position, * and ScaleType. * * @param */ public void setZoom(TouchImageView img) { PointF center = img.getScrollPosition(); setZoom(img.getCurrentZoom(), center.x, center.y, img.getScaleType()); } /** * Return the point at the center of the zoomed image. The PointF coordinates range * in value between 0 and 1 and the focus point is denoted as a fraction from the left * and top of the view. For example, the top left corner of the image would be (0, 0). * And the bottom right corner would be (1, 1). * * @return PointF representing the scroll position of the zoomed image. */ public PointF getScrollPosition() { Drawable drawable = getDrawable(); if (drawable == null) { return null; } int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); PointF point = transformCoordTouchToBitmap(viewWidth / 2, viewHeight / 2, true); point.x /= drawableWidth; point.y /= drawableHeight; return point; } /** * Set the focus point of the zoomed image. The focus points are denoted as a fraction from the * left and top of the view. The focus points can range in value between 0 and 1. * * @param focusX * @param focusY */ public void setScrollPosition(float focusX, float focusY) { setZoom(normalizedScale, focusX, focusY); } /** * Performs boundary checking and fixes the image matrix if it * is out of bounds. */ private void fixTrans() { matrix.getValues(m); float transX = m[Matrix.MTRANS_X]; float transY = m[Matrix.MTRANS_Y]; float fixTransX = getFixTrans(transX, viewWidth, getImageWidth()); float fixTransY = getFixTrans(transY, viewHeight, getImageHeight()); if (fixTransX != 0 || fixTransY != 0) { matrix.postTranslate(fixTransX, fixTransY); } } /** * When transitioning from zooming from focus to zoom from center (or vice versa) * the image can become unaligned within the view. This is apparent when zooming * quickly. When the content size is less than the view size, the content will often * be centered incorrectly within the view. fixScaleTrans first calls fixTrans() and * then makes sure the image is centered correctly within the view. */ private void fixScaleTrans() { fixTrans(); matrix.getValues(m); if (getImageWidth() < viewWidth) { m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2; } if (getImageHeight() < viewHeight) { m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2; } matrix.setValues(m); } private float getFixTrans(float trans, float viewSize, float contentSize) { float minTrans, maxTrans; if (contentSize <= viewSize) { minTrans = 0; maxTrans = viewSize - contentSize; } else { minTrans = viewSize - contentSize; maxTrans = 0; } if (trans < minTrans) return -trans + minTrans; if (trans > maxTrans) return -trans + maxTrans; return 0; } private float getFixDragTrans(float delta, float viewSize, float contentSize) { if (contentSize <= viewSize) { return 0; } return delta; } private float getImageWidth() { return matchViewWidth * normalizedScale; } private float getImageHeight() { return matchViewHeight * normalizedScale; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Drawable drawable = getDrawable(); if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) { setMeasuredDimension(0, 0); return; } int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); viewWidth = setViewSize(widthMode, widthSize, drawableWidth); viewHeight = setViewSize(heightMode, heightSize, drawableHeight); // // Set view dimensions // setMeasuredDimension(viewWidth, viewHeight); // // Fit content within view // fitImageToView(); } /** * If the normalizedScale is equal to 1, then the image is made to fit the screen. Otherwise, * it is made to fit the screen according to the dimensions of the previous image matrix. This * allows the image to maintain its zoom after rotation. */ private void fitImageToView() { Drawable drawable = getDrawable(); if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) { return; } if (matrix == null || prevMatrix == null) { return; } int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); // // Scale image for view // float scaleX = (float) viewWidth / drawableWidth; float scaleY = (float) viewHeight / drawableHeight; switch (mScaleType) { case CENTER: scaleX = scaleY = 1; break; case CENTER_CROP: scaleX = scaleY = Math.max(scaleX, scaleY); break; case CENTER_INSIDE: scaleX = scaleY = Math.min(1, Math.min(scaleX, scaleY)); case FIT_CENTER: scaleX = scaleY = Math.min(scaleX, scaleY); break; case FIT_XY: break; default: // // FIT_START and FIT_END not supported // throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END"); } // // Center the image // float redundantXSpace = viewWidth - (scaleX * drawableWidth); float redundantYSpace = viewHeight - (scaleY * drawableHeight); matchViewWidth = viewWidth - redundantXSpace; matchViewHeight = viewHeight - redundantYSpace; if (!isZoomed() && !imageRenderedAtLeastOnce) { // // Stretch and center image to fit view // matrix.setScale(scaleX, scaleY); matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2); normalizedScale = 1; } else { // // These values should never be 0 or we will set viewWidth and viewHeight // to NaN in translateMatrixAfterRotate. To avoid this, call savePreviousImageValues // to set them equal to the current values. // if (prevMatchViewWidth == 0 || prevMatchViewHeight == 0) { savePreviousImageValues(); } prevMatrix.getValues(m); // // Rescale Matrix after rotation // m[Matrix.MSCALE_X] = matchViewWidth / drawableWidth * normalizedScale; m[Matrix.MSCALE_Y] = matchViewHeight / drawableHeight * normalizedScale; // // TransX and TransY from previous matrix // float transX = m[Matrix.MTRANS_X]; float transY = m[Matrix.MTRANS_Y]; // // Width // float prevActualWidth = prevMatchViewWidth * normalizedScale; float actualWidth = getImageWidth(); translateMatrixAfterRotate(Matrix.MTRANS_X, transX, prevActualWidth, actualWidth, prevViewWidth, viewWidth, drawableWidth); // // Height // float prevActualHeight = prevMatchViewHeight * normalizedScale; float actualHeight = getImageHeight(); translateMatrixAfterRotate(Matrix.MTRANS_Y, transY, prevActualHeight, actualHeight, prevViewHeight, viewHeight, drawableHeight); // // Set the matrix to the adjusted scale and translate values. // matrix.setValues(m); } fixTrans(); setImageMatrix(matrix); } /** * Set view dimensions based on layout params * * @param mode * @param size * @param drawableWidth * @return */ private int setViewSize(int mode, int size, int drawableWidth) { int viewSize; switch (mode) { case MeasureSpec.EXACTLY: viewSize = size; break; case MeasureSpec.AT_MOST: viewSize = Math.min(drawableWidth, size); break; case MeasureSpec.UNSPECIFIED: viewSize = drawableWidth; break; default: viewSize = size; break; } return viewSize; } ........

由于SOF限制为30000个字符,它可能会错过最后一行代码,请从上面的链接中取出类。

在XML中使用它:

https://gist.github.com/myinnos/e14b750be9503a2b2611f5a5a74a9e6c
© www.soinside.com 2019 - 2024. All rights reserved.