Android ImageView Zoom-in and Zoom-Out

前端 未结 23 1892
死守一世寂寞
死守一世寂寞 2020-11-22 09:31

I want to Zoom-in and Zoom-out an Android ImageView. I tried most of the samples but in all of them the image in the ImageView itself is getting Zoomed-in and Zoomed-out, wh

相关标签:
23条回答
  • 2020-11-22 10:25

    This code works and implement the double tap to return to original image size.

    1st step - In your xml layout put this:

    <com.****.*****.TouchImageView
        android:id="@+id/action_infolinks_splash"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@mipmap/myinfolinks_splash"
        android:layout_gravity="center"
        android:gravity="center"
        android:scaleType="fitCenter"
        android:contentDescription="@string/aboutSupport_description_image"/>
    

    2nd step- Create a file (TouchImageView.java) with the TouchImageView class:

    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Matrix;
    import android.graphics.PointF;
    import android.util.AttributeSet;
    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 {
    
        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;
        float redundantXSpace, redundantYSpace, origRedundantXSpace, origRedundantYSpace;
    
        int viewWidth, viewHeight;
        static final int CLICK = 3;
        static final float SAVE_SCALE = 1f;
        float saveScale = SAVE_SCALE;
        protected float origWidth, origHeight;
        int oldMeasuredWidth, oldMeasuredHeight;
        float origScale, bottom, origBottom, right, origRight;
    
        ScaleGestureDetector mScaleDetector;
        GestureDetector mGestureDetector;
    
        Context context;
    
        public TouchImageView(Context context) {
            super(context);
            sharedConstructing(context);
        }
    
        public TouchImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            sharedConstructing(context);
        }
    
        private void sharedConstructing(Context context) {
            super.setClickable(true);
            this.context = context;
            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) {
    
                    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;
                        matrix = new Matrix();
                        matrix.setScale(origScale, origScale);
                        matrix.postTranslate(origRedundantXSpace, origRedundantYSpace);
                        setImageMatrix(matrix);
                        invalidate();
                        return true;
                    }
    
                    mScaleDetector.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
                }
    
            });
    
            mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onDoubleTapEvent(MotionEvent e) {
                    return true;
                }
            });
        }
    
        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 = detector.getScaleFactor();
                //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 = viewWidth * saveScale - viewWidth - (2 * redundantXSpace * saveScale);
                bottom = viewHeight * saveScale - viewHeight - (2 * redundantYSpace * saveScale);
    
                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;
                int bmWidth,bmHeight;
    
                Bitmap bm = BitmapFactory.decodeResource(context.getResources(), R.mipmap.myinfolinks_splash);
                bmWidth = bm.getWidth();
                bmHeight = bm.getHeight();
    
                int  w = bmWidth;
                int  h = bmHeight;
                viewWidth = resolveSize(w, widthMeasureSpec);
                viewHeight = resolveSize(h, heightMeasureSpec);
    
                float scaleX = (float) viewWidth / (float) bmWidth;
                float scaleY = (float) viewHeight / (float) bmHeight;
                scale = Math.min(scaleX, scaleY);
                matrix.setScale(scale, scale);
                saveScale = SAVE_SCALE;
                origScale = scale;
    
                // Center the image
                redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
                redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
                redundantYSpace /= (float) 2;
                redundantXSpace /= (float) 2;
    
                origRedundantXSpace = redundantXSpace;
                origRedundantYSpace = redundantYSpace;
    
                matrix.postTranslate(redundantXSpace, redundantYSpace);
    
                origWidth = viewWidth - 2 * redundantXSpace;
                origHeight = viewHeight - 2 * redundantYSpace;
    
                right = viewWidth * saveScale - viewWidth - (2 * redundantXSpace * saveScale);
                bottom = viewHeight * saveScale - viewHeight - (2 * redundantYSpace * saveScale);
                origRight = right;
                origBottom = bottom;
    
                setImageMatrix(matrix);
            }
            fixTrans();
        }
    }
    

    And finally, make the call in your main activity:

    TouchImageView imgDisplay = (TouchImageView) messageView.findViewById(R.id.id_myImage);
    imgDisplay.setMaxZoom(2f);
    imgDisplay.setImageResource(R.drawable.myImage);
    

    I saw lots of code and after my adjustments it's working. Enjoy!

    0 讨论(0)
  • 2020-11-22 10:25

    ZoomLib Link: https://drive.google.com/uc?export=download&id=0B34PUThnUsjVaHpkaGk0Z1hSRU0

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.dummy_layout_for_zooming);// Activity layout 
    mZoomLinearLayout = (LinearLayout) findViewById(R.id.mZoomLinearLayout);// LinearLayout inside Activity layout
    View v = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.layout_for_zoom, null, false);// View wants to zoom
    v.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
    ZoomView zoomView = new ZoomView(this);// intialize lib
    zoomView.addView(v);
    mZoomLinearLayout.addView(zoomView);
    }
    
    0 讨论(0)
  • 2020-11-22 10:26

    I have improved the answer I got from stack for flawless ZOOM (two finger) / ROTATION (two finger) / DRAG (Single finger).

    //============================XML code==================

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.flochat.imageviewzoomforstack.MainActivity">
    
        <ImageView
            android:id="@+id/imageview_trash"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/trash" />
    
    </LinearLayout>
    

    //============================Java code==========================

    public class MainActivity extends AppCompatActivity {
    
        ImageView photoview2;
        float[] lastEvent = null;
        float d = 0f;
        float newRot = 0f;
        private boolean isZoomAndRotate;
        private boolean isOutSide;
        private static final int NONE = 0;
        private static final int DRAG = 1;
        private static final int ZOOM = 2;
        private int mode = NONE;
        private PointF start = new PointF();
        private PointF mid = new PointF();
        float oldDist = 1f;
        private float xCoOrdinate, yCoOrdinate;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
            setContentView(R.layout.activity_main);
    
            photoview2 = findViewById(R.id.imageview_trash);
    
            photoview2.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    ImageView view = (ImageView) v;
                    view.bringToFront();
                    viewTransformation(view, event);
                    return true;
                }
            });
        }
    
    
        private void viewTransformation(View view, MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                xCoOrdinate = view.getX() - event.getRawX();
                yCoOrdinate = view.getY() - event.getRawY();
    
                    start.set(event.getX(), event.getY());
                    isOutSide = false;
                    mode = DRAG;
                    lastEvent = null;
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    oldDist = spacing(event);
                    if (oldDist > 10f) {
                        midPoint(mid, event);
                        mode = ZOOM;
                    }
    
                    lastEvent = new float[4];
                    lastEvent[0] = event.getX(0);
                    lastEvent[1] = event.getX(1);
                    lastEvent[2] = event.getY(0);
                    lastEvent[3] = event.getY(1);
                    d = rotation(event);
                    break;
                case MotionEvent.ACTION_UP:
                    isZoomAndRotate = false;
                    if (mode == DRAG) {
                        float x = event.getX();
                        float y = event.getY();
                    }
                case MotionEvent.ACTION_OUTSIDE:
                    isOutSide = true;
                    mode = NONE;
                    lastEvent = null;
                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    lastEvent = null;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (!isOutSide) {
                        if (mode == DRAG) {
                            isZoomAndRotate = false;
                            view.animate().x(event.getRawX() + xCoOrdinate).y(event.getRawY() + yCoOrdinate).setDuration(0).start();
                        }
                        if (mode == ZOOM && event.getPointerCount() == 2) {
                            float newDist1 = spacing(event);
                            if (newDist1 > 10f) {
                                float scale = newDist1 / oldDist * view.getScaleX();
                                view.setScaleX(scale);
                                view.setScaleY(scale);
                            }
                            if (lastEvent != null) {
                                newRot = rotation(event);
                                view.setRotation((float) (view.getRotation() + (newRot - d)));
                            }
                        }
                    }
                    break;
            }
        }
    
        private float rotation(MotionEvent event) {
            double delta_x = (event.getX(0) - event.getX(1));
            double delta_y = (event.getY(0) - event.getY(1));
            double radians = Math.atan2(delta_y, delta_x);
            return (float) Math.toDegrees(radians);
        }
    
        private float spacing(MotionEvent event) {
            float x = event.getX(0) - event.getX(1);
            float y = event.getY(0) - event.getY(1);
            return (int) Math.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);
        }
    
    }
    

    //========================Just pass any view which you want to zoom/rotate/drag to viewTransformation() method. Very applicable for textview zoom. It will not pixelate text.

    0 讨论(0)
  • 2020-11-22 10:29

    You should put the image in webview and work with that. Zoom in / out controls are available in webview.

    • http://xjaphx.wordpress.com/2011/09/18/using-webview-as-a-image-zoom-view-control/
    • http://androidforums.com/android-lounge/121522-how-display-image-sdcard-webview.html
    0 讨论(0)
  • 2020-11-22 10:30

    This is yet another implementation based on the code posted by Nicolas Tyler.

    The following bugs are fixed:

    • Setting minScale to a number less than 1 now works
    • You don't need to use setImageBitmap() to set the image (you can use, for example setImageResource()
    • All constructors now work properly

    The following things, amongst others are tidied up:

    • An OnTouchListener is not used, it's not necessary because the the class can just implement the onTouchEvent() method.

    • The assignment right = width * saveScale - width - (2 * redundantXSpace * saveScale); has been simplified to right = (originalBitmapWidth * saveScale) - width Which, in my option, is much less confusing.

    • Some member variables are removed (more state can probably be removed from this class)

    It's not perfect but here you go:

    import android.content.Context;
    import android.graphics.Matrix;
    import android.graphics.PointF;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.widget.ImageView;
    
    /**
     * Created by alex on 23/02/16.
     * Based on code posted by Nicolas Tyler here:
     * https://stackoverflow.com/questions/6650398/android-imageview-zoom-in-and-zoom-out
     */
    public class ZoomableImageView extends ImageView {
    
        private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    
            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                mode = ZOOM;
                return true;
            }
    
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                float scaleFactor = detector.getScaleFactor();
                float newScale = saveScale * scaleFactor;
                if (newScale < maxScale && newScale > minScale) {
                    saveScale = newScale;
                    float width = getWidth();
                    float height = getHeight();
                    right = (originalBitmapWidth * saveScale) - width;
                    bottom = (originalBitmapHeight * saveScale) - height;
    
                    float scaledBitmapWidth = originalBitmapWidth * saveScale;
                    float scaledBitmapHeight = originalBitmapHeight * saveScale;
    
                    if (scaledBitmapWidth <= width || scaledBitmapHeight <= height) {
                        matrix.postScale(scaleFactor, scaleFactor, width / 2, height / 2);
                    } else {
                        matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
                    }
                }
                return true;
            }
    
        }
    
        static final int NONE = 0;
        static final int DRAG = 1;
        static final int ZOOM = 2;
        static final int CLICK = 3;
    
        private int mode = NONE;
    
        private Matrix matrix = new Matrix();
    
        private PointF last = new PointF();
        private PointF start = new PointF();
        private float minScale = 0.5f;
        private float maxScale = 4f;
        private float[] m;
    
        private float redundantXSpace, redundantYSpace;
        private float saveScale = 1f;
        private float right, bottom, originalBitmapWidth, originalBitmapHeight;
    
        private ScaleGestureDetector mScaleDetector;
    
        public ZoomableImageView(Context context) {
            super(context);
            init(context);
        }
    
        public ZoomableImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public ZoomableImageView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
    
        private void init(Context context) {
            super.setClickable(true);
            mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
            m = new float[9];
            setImageMatrix(matrix);
            setScaleType(ScaleType.MATRIX);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int bmHeight = getBmHeight();
            int bmWidth = getBmWidth();
    
            float width = getMeasuredWidth();
            float height = getMeasuredHeight();
            //Fit to screen.
            float scale = width > height ? height / bmHeight :  width / bmWidth;
    
            matrix.setScale(scale, scale);
            saveScale = 1f;
    
            originalBitmapWidth = scale * bmWidth;
            originalBitmapHeight = scale * bmHeight;
    
            // Center the image
            redundantYSpace = (height - originalBitmapHeight);
            redundantXSpace = (width - originalBitmapWidth);
    
            matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2);
    
            setImageMatrix(matrix);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mScaleDetector.onTouchEvent(event);
    
            matrix.getValues(m);
            float x = m[Matrix.MTRANS_X];
            float y = m[Matrix.MTRANS_Y];
            PointF curr = new PointF(event.getX(), event.getY());
    
            switch (event.getAction()) {
                //when one finger is touching
                //set the mode to DRAG
                case MotionEvent.ACTION_DOWN:
                    last.set(event.getX(), event.getY());
                    start.set(last);
                    mode = DRAG;
                    break;
                //when two fingers are touching
                //set the mode to ZOOM
                case MotionEvent.ACTION_POINTER_DOWN:
                    last.set(event.getX(), event.getY());
                    start.set(last);
                    mode = ZOOM;
                    break;
                //when a finger moves
                //If mode is applicable move image
                case MotionEvent.ACTION_MOVE:
                    //if the mode is ZOOM or
                    //if the mode is DRAG and already zoomed
                    if (mode == ZOOM || (mode == DRAG && saveScale > minScale)) {
                        float deltaX = curr.x - last.x;// x difference
                        float deltaY = curr.y - last.y;// y difference
                        float scaleWidth = Math.round(originalBitmapWidth * saveScale);// width after applying current scale
                        float scaleHeight = Math.round(originalBitmapHeight * saveScale);// height after applying current scale
    
                        boolean limitX = false;
                        boolean limitY = false;
    
                        //if scaleWidth is smaller than the views width
                        //in other words if the image width fits in the view
                        //limit left and right movement
                        if (scaleWidth < getWidth() && scaleHeight < getHeight()) {
                            // don't do anything
                        }
                        else if (scaleWidth < getWidth()) {
                            deltaX = 0;
                            limitY = true;
                        }
                        //if scaleHeight is smaller than the views height
                        //in other words if the image height fits in the view
                        //limit up and down movement
                        else if (scaleHeight < getHeight()) {
                            deltaY = 0;
                            limitX = true;
                        }
                        //if the image doesnt fit in the width or height
                        //limit both up and down and left and right
                        else {
                            limitX = true;
                            limitY = true;
                        }
    
                        if (limitY) {
                            if (y + deltaY > 0) {
                                deltaY = -y;
                            } else  if (y + deltaY < -bottom) {
                                deltaY = -(y + bottom);
                            }
    
                        }
    
                        if (limitX) {
                            if (x + deltaX > 0) {
                                deltaX = -x;
                            } else if (x + deltaX < -right) {
                                deltaX = -(x + right);
                            }
    
                        }
                        //move the image with the matrix
                        matrix.postTranslate(deltaX, deltaY);
                        //set the last touch location to the current
                        last.set(curr.x, curr.y);
                    }
                    break;
                //first finger is lifted
                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;
                // second finger is lifted
                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    break;
            }
            setImageMatrix(matrix);
            invalidate();
            return true;
        }
    
        public void setMaxZoom(float x) {
            maxScale = x;
        }
    
        private int getBmWidth() {
            Drawable drawable = getDrawable();
            if (drawable != null) {
                return drawable.getIntrinsicWidth();
            }
            return 0;
        }
    
        private int getBmHeight() {
            Drawable drawable = getDrawable();
            if (drawable != null) {
                return drawable.getIntrinsicHeight();
            }
            return 0;
        }
    }
    
    0 讨论(0)
提交回复
热议问题