Search code examples
javaandroidimageviewpinchzoom

ImageView zoom and set another image problem


I have succeeded to zoom imageView by this site idea:

https://www.androidhive.info/2013/09/android-fullscreen-image-slider-with-swipe-and-pinch-zoom-gestures/

the problem that appears to me now is: i use an next and previous buttons to load images in the same image view when that done the new image loaded zoomed in. i need to view it in normal size and let user to zoom it if he want.

import android.annotation.SuppressLint;
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.view.View;
import android.widget.ImageView;

@SuppressLint("AppCompatCustomView")
public class MyTouchImageView extends ImageView {
    // We can be in one of these 3 states
    private  static final int NONE = 0;
    private  static final int DRAG = 1;
    private  static final int CLICK = 3;
    private static final int ZOOM = 2;
    private float origWidth, origHeight;
    private Matrix matrix;
    private  int mode = NONE;
    private PointF last = new PointF();
    private PointF start = new PointF();
    private  float minScale = 1f;
    private float maxScale = 3f;
    private  float[] m;
    private  int viewWidth, viewHeight;
    private float saveScale = 1f;
    private  int oldMeasuredWidth, oldMeasuredHeight;

    private ScaleGestureDetector mScaleDetector;

    private  Context context;

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

    public MyTouchImageView(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);
        setMinimumHeight(800);
        setMinimumWidth(1000);
  
        setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                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
            }

        });
    }

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

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


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

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

LOADING IMAGE

private MyTouchImageView Title_images;
    Title_images = findViewById(R.id.title_image); 
    Glide.with(Objects.requireNonNull(StudentInsideExam.this)).load(examInsideDatas.get(ShownPointIndexInArray).getImageUrls()).into(Title_images);

XML:

 <FrameLayout
    android:id="@+id/container1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="vertical">

        <com.ayman.learningway.MyTouchImageView
            android:id="@+id/title_image"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:layout_marginTop="@dimen/smallMargin"
            android:background="@color/tan_background"
            android:scaleType="matrix"
            android:visibility="gone" />

    </LinearLayout>


</FrameLayout>

Solution

  • In order to avoid using the same scale of the previous image when you navigate to the next image, you need to reset the previous image to its original scale.

    If you notice when you first load your image into your MyTouchImageView, it loads with its original scale which is normally (1f), you can find that in the MyTouchImageView with the statement private float saveScale = 1f;

    Also when you pinch zoom your image, then onScale() of the ScaleGestureDetector.SimpleOnScaleGestureListener within the MyTouchImageView is triggered.

    So what you need to do to reset the image to its original scale is to reverse the last operation happened within onScale(). Which is normally allows you to back to a scale of 1f.

    To to that add the below method to your MyTouchImageView:

    public void resetToOriginalScale() {
    
        // reverse the operation happened on onScale() method
        float scaleFactor = (float) 1.0 / saveScale;
        // reset the scale to the original scale
        saveScale = 1f;
        matrix.postScale(scaleFactor, scaleFactor, viewWidth / 2, viewHeight / 2);
    
        fixTrans();
    
        // Simulating a touch event within the ImageView in order to allow it to redraw with the original scale
        long downTime = SystemClock.uptimeMillis();
        long eventTime = SystemClock.uptimeMillis() + 100;
        int metaState = 0;
        MotionEvent motionEvent = MotionEvent.obtain(
                downTime,
                eventTime,
                MotionEvent.ACTION_UP,
                0,
                0,
                metaState
        );
    
        dispatchTouchEvent(motionEvent);
    
    }
    
    

    Then whenever you navigate to a new image, you first need to reset the scale of the current image by calling resetToOriginalScale():

    mImageView = findViewById(R.id.title_image);
    mImageView.resetToOriginalScale();
    Glide.with(MainActivity.this).load(new_image_url).centerCrop().into(mImageView);
    

    Use .centerCrop() with all your Glide invocations.

    Hope this solves your problem.