Question

I want to implement Pinch Zoom on Imageview, with in View Pager similar to Default Android Gallery. I have found multiple source over GitHub, But the zoom and sliding just work for only first image.

What I have tried:

1.) TouchImageView

2.) PhotoView

3.) Android Touch Gallery

All the above links works fine for single image view. But when it comes to Images in View pager, They have some glitches and only works fine for first image in the View Pager. When we scroll over to 3rd 4th image in view pager, Dragging functionality not working as expected if the image is zoomed.

Please if any one knows any good library for doing this, then provide me the link for them.

Was it helpful?

Solution

EDIT 2: Example code has been pushed to the master branch of TouchImageView. Here is a link to the example activity and a link to the ExtendedViewPager.


EDIT: added code adapting the example link to TouchImageView. Note: you will need the latest code, which is currently in the dev branch. In the future, this will be included in v1.2.0. You know you have the latest code if TouchImageView overrides canScrollHorizontally.

Step 1: Extend ViewPager and override canScroll to call canScrollHorizontallyFroyo.

public class ExtendedViewPager extends ViewPager {

public ExtendedViewPager(Context context) {
    super(context);
}

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

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if (v instanceof TouchImageView) {
        return ((TouchImageView) v).canScrollHorizontallyFroyo(-dx);
    } else {
        return super.canScroll(v, checkV, dx, x, y);
    }
}

}

Step 2: Modify TouchImageView by adding canScrollHorizontallyFroyo:

public boolean canScrollHorizontallyFroyo(int direction) {
    return canScrollHorizontally(direction);
}

Step 3: Your activity

public class TouchImageViewActivity extends Activity {

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ExtendedViewPager mViewPager = (ExtendedViewPager) findViewById(R.id.view_pager);
        setContentView(mViewPager);
        mViewPager.setAdapter(new TouchImageAdapter());
    }

    static class TouchImageAdapter extends PagerAdapter {

            private static int[] images = { R.drawable.img1, R.drawable.img2, R.drawable.img3 };

            @Override
            public int getCount() {
                    return images.length;
            }

            @Override
            public View instantiateItem(ViewGroup container, int position) {
                    TouchImageView img = new TouchImageView(container.getContext());
                    img.setImageResource(images[position]);
                    container.addView(img, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                    return img;
            }

            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                    container.removeView((View) object);
            }

            @Override
            public boolean isViewFromObject(View view, Object object) {
                    return view == object;
            }

    }
}

Step 4: main.xml

<com.example.touch.ExtendedViewPager 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/view_pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

TouchImageView is actually my project. I currently have a fix in the dev branch for integration with ViewPagers, which will be pushed to master in an upcoming release. Unfortunately, this fix is only applicable for API 14 and greater since honeycomb and earlier do not call canScrollHorizontally. If you need to support older APIs, then you will need to implement a workaround in your ViewPager. Here is an example.

OTHER TIPS

I found pretty solution with ImageViewZoom library. In order to scroll zoomed image in ViewPager I created own ViewPager:

public class ExtendedViewPager extends ViewPager {

    public ExtendedViewPager(Context context) {
        super(context);
    }

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

    @Override
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        if (v instanceof ImageViewTouch) {
            return ((ImageViewTouch) v).canScroll(dx);
        } else {
            return super.canScroll(v, checkV, dx, x, y);
        }
    }
}

See more https://gist.github.com/atermenji/3781644

After several hours of testing the solutions above I have finally found the awesome Subsampling Scale Image View library, which works even with standard ViewPager from Android Support Package.

My solution using ImageViewZoom Library is based on this custom ViewPager:

public class ImageViewTouchViewPager extends ViewPager {

    public ImageViewTouchViewPager(Context context) {
        super(context);
    }

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

    @Override
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        if (v instanceof ImageViewTouch) {
            ImageViewTouch imageViewTouch = (ImageViewTouch)v;
            if (imageViewTouch.getScale() == imageViewTouch.getMinScale()) {
                return super.canScroll(v, checkV, dx, x, y);
            }
            return imageViewTouchCanScroll(imageViewTouch, dx);
        } else {
            return super.canScroll(v, checkV, dx, x, y);
        }
    }


    /**
     * Determines whether the ImageViewTouch can be scrolled.
     *
     * @param direction - positive direction value means scroll from right to left,
     *                  negative value means scroll from left to right
     * @return true if there is some more place to scroll, false - otherwise.
     */
    private boolean imageViewTouchCanScroll(ImageViewTouch v, int direction){
        RectF bitmapRect = v.getBitmapRect();
        Rect imageViewRect = new Rect();
        getGlobalVisibleRect(imageViewRect);

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

        if (direction < 0) {
            return Math.abs(bitmapRect.right - imageViewRect.right) > 1.0f;
        }else {
            return Math.abs(bitmapRect.left - imageViewRect.left) > 1.0f;
        }

    }
}

I corrected the previous solution . You can scroll page , when ImageViewTouch is mode zoom.

public class ImageViewTouchViewPager extends ViewPager {

public ImageViewTouchViewPager(Context context) {
    super(context);
}

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

@Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
    if (v instanceof ImageViewTouch) {
        ImageViewTouch imageViewTouch = (ImageViewTouch)v;
        return imageViewTouchCanScroll(imageViewTouch, dx);
    } else {
        return super.canScroll(v, checkV, dx, x, y);
    }
}


/**
 * Determines whether the ImageViewTouch can be scrolled.
 *
 * @param direction - positive direction value means scroll from right to left,
 *                  negative value means scroll from left to right
 * @return true if there is some more place to scroll, false - otherwise.
 */
private boolean imageViewTouchCanScroll(ImageViewTouch imageViewTouch, int direction){
    int widthScreen = getWidthScreen();

    RectF bitmapRect = imageViewTouch.getBitmapRect();
    Rect imageViewRect = new Rect();
    getGlobalVisibleRect(imageViewRect);

    int widthBitmapViewTouch = (int)bitmapRect.width();

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

    if(widthBitmapViewTouch < widthScreen){
        return false;
    }

    if (direction < 0) {
        return Math.abs(bitmapRect.right - imageViewRect.right) > 1.0f;
    }else {
        return Math.abs(bitmapRect.left - imageViewRect.left) > 1.0f;
    }

}

private int getWidthScreen(){
    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();

    Point size = new Point();
    display.getSize(size);
    return size.x;
}

}

For Those who are struggling to disable viewpager when the image is in pinched to zoom state & enable when the image is in original state. I just made some changes as answered by Mike.

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.viewpager.widget.ViewPager


class DCExtendedViewPager : ViewPager {


    private val TAG = DCExtendedViewPager::class.java.simpleName

    private var onImageState: OnImageState? = null

    private var touchImageViewCustom: DCTouchImageViewLatest? = null


    var isScroll: Boolean = true

    interface OnImageState {
        fun checkImageState(isImageInOriginalState: Boolean)
    }


    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)


    override fun canScroll(view: View, checkV: Boolean, dx: Int, x: Int, y: Int): Boolean {
        return if (view is DCTouchImageViewLatest) {
            //   touchImageView=view
            // canScrollHorizontally is not supported for Api < 14. To get around this issue,
            // ViewPager is extended and canScrollHorizontallyFroyo, a wrapper around
            // canScrollHorizontally supporting Api >= 8, is called.
            Log.e("ExtendedViewPager", "canScroll zoomedRect" + view.zoomedRect)

            view.canScrollHorizontallyFroyo(-dx)

        } else {
            super.canScroll(view, checkV, dx, x, y)
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        Log.e(TAG, "onTouchEventenable" + isScroll)
        return if (isScroll) {
            super.onTouchEvent(event)
        } else false

    }

    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {

        Log.e(TAG, "onInterceptTouchEvent")

        Log.e(TAG, "currenrLayoutView called")
        val currenrLayoutView = getCurrentParentView()
        getTouchImageViewInstance(currenrLayoutView!!)

        return if (isScroll) {
            super.onInterceptTouchEvent(event)
        } else false

    }


    fun isViewPagerScrollValid(): Boolean {

        Log.e(TAG, "getFocusedChild()" + focusedChild)


        val currenrLayoutView = getCurrentParentView()


        var zoomRect = getTouchImageViewInstance(currenrLayoutView!!)?.zoomedRect
        var orgzoomRect = getTouchImageViewInstance(currenrLayoutView)?.originalRectF


        Log.e(TAG, "onInterceptTouchEvent zoomRect" + zoomRect)
        Log.e(TAG, "onInterceptTouchEvent orgzoomRect" + orgzoomRect)
        Log.e(TAG, "onInterceptTouchEvent onImageState" + onImageState)
        var scrollEnable = (zoomRect == orgzoomRect)


        // postLater(getTouchImageViewInstance(currenrLayoutView!!)!!)

        onImageState?.checkImageState(scrollEnable)
        Log.e(TAG, "onInterceptTouchEvent" + scrollEnable)


        return scrollEnable

    }


    fun setImageStateListner(onImageState: OnImageState) {
        this.onImageState = onImageState
    }


    fun getTouchImageViewInstance(accessingView: View): DCTouchImageViewLatest? {


        if (touchImageViewCustom == null) {

            try {
                for (index in 0 until (accessingView as ViewGroup).childCount) {

                    var nextChild = accessingView.getChildAt(index)

                    Log.e(TAG, "nextChild" + nextChild)

                    if (nextChild is ViewGroup) {
                        getTouchImageViewInstance(nextChild)
                    } else if (nextChild is View) {
                        if (nextChild is DCTouchImageViewLatest) {
                            touchImageViewCustom = nextChild
                            setListner()
                            break
                        }
                    }
                }

            } catch (ex: Exception) {
                ex.printStackTrace()
            }


        }


        Log.e(TAG, "getTouchImageViewInstance" + touchImageViewCustom)

        return touchImageViewCustom
    }

    private fun setListner() {

        touchImageViewCustom?.setOnDCTouchImageViewLatestListener(object : DCTouchImageViewLatest.OnDCTouchImageViewLatestListener {
            override fun onMove() {
                Log.e(TAG, "onMove Called")
                isScroll = isViewPagerScrollValid()
            }

        })

    }


    //Call this method from onPageSelected of viewpager
    fun viewPageChanged() {
        Log.e(TAG, "viewPageChanged called")
        touchImageViewCustom = null
    }


    fun getCurrentParentView(): View? {
        try {
            Log.e(TAG, "getCurrentView called")
            val currentItem = currentItem
            for (i in 0 until childCount) {
                val child = getChildAt(i)
                val layoutParams = child.layoutParams as ViewPager.LayoutParams

                val f = layoutParams.javaClass.getDeclaredField("position") //NoSuchFieldException
                f.isAccessible = true
                val position = f.get(layoutParams) as Int //IllegalAccessException

                Log.e(TAG, "currentItem" + currentItem)

                if (!layoutParams.isDecor && currentItem == position) {
                    Log.e(TAG, "getCurrentView" + child)
                    return child
                }
            }
        } catch (e: NoSuchFieldException) {
            Log.e(TAG, e.toString())
        } catch (e: IllegalArgumentException) {
            Log.e(TAG, e.toString())
        } catch (e: IllegalAccessException) {
            Log.e(TAG, e.toString())
        }

        return null
    }


}

In TouchImageView class, call getOriginalRectF() from setImageBitmap, setImageDrawable & setImageURI.

 public RectF getOriginalRectF(){

        Log.e(TAG,"getOriginalRectF called viewWidth"+viewWidth);
        Log.e(TAG,"getOriginalRectF called viewHeight"+viewHeight);

        if(originalRectF==null && viewHeight>0 && viewWidth>0){
            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 = getDrawableWidth(getDrawable());
            float h = getDrawableHeight(getDrawable());


            Log.e(TAG,"getOriginalRectF height"+h);
            Log.e(TAG,"getOriginalRectF width"+w);

            Log.e("getOriginalRectF","getZoomedRect topLeft"+topLeft.x +"-"+topLeft.y);
            Log.e("getOriginalRectF","getZoomedRect bottomRight"+bottomRight.x +"-"+bottomRight.y);


            originalRectF=new  RectF(topLeft.x / w, topLeft.y / h, bottomRight.x / w, bottomRight.y / h);


        }


        return originalRectF;

    }

Any of the above solution did not work for me. It worked for me when I used the below ImageView:

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;

@SuppressLint("AppCompatCustomView")
public class TouchImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener,View.OnTouchListener {
    private final static int SINGLE_TOUCH = 1; //Single point
    private final static int DOUBLE_TOUCH = 2; //Double finger

    //Multi-finger touch mode, single finger, double finger
    private int mode;

    //Distance between two finger touch points
    private float oldDist;
    private float newDist;

    /**
     * Maximum zoom level
     */
    private static final float MAX_SCALE = 5f;
    /**
     * Double-click the zoom level
     */
    private  float mDoubleClickScale = 2;


    /**
     * Scales when initialization, if the picture is wide or higher than the screen, this value will be less than 0
     */
    private float initScale = 1.0f;
    private boolean once = true;
    private RectF rectF;

    /**
     * Double-click detection
     */
    private GestureDetector mGestureDetector;
    private int x = 0;
    private int y = 0;

    private Point mPoint = new Point();

    private final Matrix matrix = new Matrix();
    private Matrix oldMatrix = new Matrix();

    private ValueAnimator animator;

    public TouchImageView(Context context) {
        this(context, null);
    }

    public TouchImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setScaleType(ScaleType.MATRIX);
        setOnTouchListener(this);
        /**
         * Double click to implement the picture to zoom
         */
        mGestureDetector = new GestureDetector(context,
                new GestureDetector.SimpleOnGestureListener() {
                    @Override
                    public boolean onDoubleTap(MotionEvent e) {
                        changeViewSize(e);
                        return true;
                    }
                });
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        rectF = getMatrixRectF(); //Get image boundaries
        if (mGestureDetector.onTouchEvent(event))
            return true;

        switch (event.getAction() & event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                //If the boundary of the picture is overstelled, then intercept the event, not letting ViewPager processing
                if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                mode = SINGLE_TOUCH;

                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode >= DOUBLE_TOUCH) //Double finger zoom
                {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    newDist = calculateDist(event); //Calculate distance
                    Point point = getMiPoint(event); //Get the midpoint coordinates between the two-hand fingers
                    if (newDist > oldDist + 1) //Pixabay (add one to prevent jitter)
                    {
                        changeViewSize(oldDist, newDist, point); //Enlarge reduction according to distance
                        oldDist = newDist;
                    }
                    if (oldDist > newDist + 1) //Narrow
                    {
                        changeViewSize(oldDist, newDist, point);
                        oldDist = newDist;
                    }
                }
                if (mode == SINGLE_TOUCH) //Drag and drop
                {
                    float dx = event.getRawX() - x;
                    float dy = event.getRawY() - y;

                    //If the boundary of the picture in the movement exceeds the screen, then intercept the event, do not let ViewPager processing
                    if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                    //If you move the picture to the right, don't intercept the event, let the ViewPager process
                    if (rectF.left >= 0 && dx > 0)
                        getParent().requestDisallowInterceptTouchEvent(false);

                    //If you move to the left, don't intercept the event, let ViewPager processing
                    if (rectF.right <= getWidth() && dx < 0)
                        getParent().requestDisallowInterceptTouchEvent(false);

                    if (getDrawable() != null) {
                        //If the image width or height does not exceed the screen, then it is forbidden to slide around or down.
                        if (rectF.width() <= getWidth())
                            dx = 0;
                        if (rectF.height() < getHeight())
                            dy = 0;

                        //If the picture moves down to the end, don't let it continue to move
                        if (rectF.top >= 0 && dy > 0)
                            dy = 0;
                        //If the picture moves up to the end, don't let it continue to move
                        if (rectF.bottom <= getHeight() && dy < 0)
                            dy = 0;

                        //When the movement distance is greater than 1, it moves because Action_Move is relatively sensitive.
                        //  The finger can only detect the jitter of the finger and let the picture move.
                        if (Math.abs(dx) > 1 || Math.abs(dy) > 1)
                            matrix.postTranslate(dx, dy);
                        setImageMatrix(matrix);
                    }
                }
                x = (int) event.getRawX();
                y = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                mode += 1;
                oldDist = calculateDist(event);
                Log.e("q", "" + "a");

                Log.e(":::", "" + event.getPointerCount() + "   " + event.getActionIndex() + "   " + event.findPointerIndex(0));
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mode -= 1;
                break;
            case MotionEvent.ACTION_UP:
                backToPosition();
                mode = 0;
                break;
            //In Action_Move, the event is intercepted, sometimes Action_up can't trigger, so add Action_Cancel
            case MotionEvent.ACTION_CANCEL:
                backToPosition();
                mode = 0;
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * Calculate the distance between two finger touch points
     */
    private float calculateDist(MotionEvent event) {

        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);

    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    /**
     * If it is separated from the screen boundary, then the image boundary is combined with the screen boundary.
     * If the finger moves quickly, the picture will have a blank distance after the picture is stopped, and then the judgment can no longer move,
     * However, it has appeared before the next judgment can continue to move.
     * So you need to reset
     */
    private void backToPosition() {
        if (rectF.left >= 0) { //Image Left boundary and screen from the screen
            matrix.postTranslate(-rectF.left, 0);
            setImageMatrix(matrix);
        }
        if (rectF.right <= getWidth()) { //Image Right Boundary and Screen Distance
            matrix.postTranslate(getWidth() - rectF.right, 0);
            setImageMatrix(matrix);
        }
        if (rectF.top >= 0) { //Image on the image and the screen from the screen
            matrix.postTranslate(0, -rectF.top);
            setImageMatrix(matrix);
        }
        if (rectF.bottom <= getHeight()) { //Image of the image and the screen
            matrix.postTranslate(0, getHeight() - rectF.bottom);
            setImageMatrix(matrix);
        }
    }


    /**
     * Get the zoom in the zoom of double finger zoom
     *
     * @return
     */
    private Point getMiPoint(MotionEvent event) {

        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);

        mPoint.set((int) x / 2, (int) y / 2);
        return mPoint;
    }

    /**
     * Double finger zoom picture
     */
    private void changeViewSize(float oldDist, float newDist, Point mPoint) {
        float scale = newDist / oldDist; //scaling ratio

        matrix.postScale(scale, scale, mPoint.x, mPoint.y);
        checkBorderAndCenterWhenScale();
        setImageMatrix(matrix);

        //Prevent reduction is less than the initial picture size, need to reset
        reSetMatrix();
        //If the zoom has been larger than the target multiple, stop, because it is possible to be exceeded, then directly zoom to the target size
        if (getMatrixValueX() >= MAX_SCALE)
        {
            matrix.postScale(MAX_SCALE/getMatrixValueX(), MAX_SCALE/getMatrixValueX(), x, y);
            checkBorderAndCenterWhenScale();
            setImageMatrix(matrix);
            return;
        }
    }

    /**
     * Double click to zoom pictures
     */
    private void changeViewSize(MotionEvent e) {

        //Get double-click coordinates
        final float x = e.getX();
        final float y = e.getY();

        //If you are still zooming, you will return directly.
        if (animator != null && animator.isRunning())
            return;

        //Judgment is a state in which it is amplified or reduced
        if (!isZoomChanged()) {
            animator = ValueAnimator.ofFloat(1.0f, 2.0f);
        } else {
            animator = ValueAnimator.ofFloat(1.0f, 0.0f);
        }
        animator.setTarget(this);
        animator.setDuration(500);
        animator.setInterpolator(new DecelerateInterpolator());
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

                Float value = (Float) animator.getAnimatedValue();
                matrix.postScale(value, value, x, y);
                checkBorderAndCenterWhenScale();
                setImageMatrix(matrix);

                /**
                 * Control reduction range
                 * If it is already less than the initial size, then restore to the initial size, then stop
                 */
                if (checkRestScale()) {
                    matrix.set(oldMatrix);
                    setImageMatrix(matrix);
                    return;
                }
                /**
                 * Control the range of amplification
                 * If the magnification of the target is already larger than the target, it is directly in the target magnification.
                 * Then stop
                 */
                if (getMatrixValueX() >= mDoubleClickScale)
                {
                    matrix.postScale(mDoubleClickScale/getMatrixValueX(), mDoubleClickScale/getMatrixValueX(), x, y);
                    checkBorderAndCenterWhenScale();
                    setImageMatrix(matrix);
                    return;
                }
            }
        });
    }

    /**
     * Judging whether the zoom level is changed
     *
     * @return  TRUE expressed the non-initial value, FALSE indicates the initial value.
     */
    private boolean isZoomChanged() {
        float[] values = new float[9];
        getImageMatrix().getValues(values);
        //Get the current X-axis scale level
        float scale = values[Matrix.MSCALE_X];
        //Get the X-axis scaling level of the template, both
        oldMatrix.getValues(values);
        return scale != values[Matrix.MSCALE_X];
    }

    /**
     * Reset Matrix
     */
    private void reSetMatrix() {
        if (checkRestScale()) {
            matrix.set(oldMatrix);
            setImageMatrix(matrix);
            return;
        }
    }

    /**
     * Setup double-click a large multiple
     */
    private void setDoubleClickScale(RectF rectF)
    {
        if(rectF.height()<getHeight()-100)
        {
            mDoubleClickScale=getHeight()/rectF.height();
        }
        else
            mDoubleClickScale=2f;
    }

    /**
     * Judging whether it needs to be reset
     *
     * @return  When the current zoom level is less than the template zoom level, reset
     */
    private boolean checkRestScale() {
        // TODO Auto-generated method stub
        float[] values = new float[9];
        getImageMatrix().getValues(values);
        //Get the current X-axis scale level
        float scale = values[Matrix.MSCALE_X];
        //Get the X-axis scaling level of the template, both
        oldMatrix.getValues(values);
        return scale < values[Matrix.MSCALE_X];
    }

    private float getMatrixValueX()
    {
        // TODO Auto-generated method stub
        float[] values = new float[9];
        getImageMatrix().getValues(values);
        //Get the current X-axis scale level
        float scale = values[Matrix.MSCALE_X];
        //Get the X-axis scaling level of the template, both
        oldMatrix.getValues(values);
        return scale / values[Matrix.MSCALE_X];
    }
    /**
     * When zooming, perform image display scope control
     */
    private void checkBorderAndCenterWhenScale()
    {

        RectF rect = getMatrixRectF();
        float deltaX = 0;
        float deltaY = 0;

        int width = getWidth();
        int height = getHeight();

        //  Control range if wide or higher than the screen
        if (rect.width() >= width)
        {
            if (rect.left > 0)
            {
                deltaX = -rect.left;
            }
            if (rect.right < width)
            {
                deltaX = width - rect.right;
            }
        }
        if (rect.height() >= height)
        {
            if (rect.top > 0)
            {
                deltaY = -rect.top;
            }
            if (rect.bottom < height)
            {
                deltaY = height - rect.bottom;
            }
        }
        //  If the width or higher is less than the screen, let it hit
        if (rect.width() < width)
        {
            deltaX = width * 0.5f - rect.right + 0.5f * rect.width();
        }
        if (rect.height() < height)
        {
            deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();
        }
        Log.e("TAG", "deltaX = " + deltaX + " , deltaY = " + deltaY);

        matrix.postTranslate(deltaX, deltaY);
        setImageMatrix(matrix);
    }
    /**
     * Get the range of images according to the Matrix of the current picture
     *
     * @return
     */
    private RectF getMatrixRectF()
    {
        RectF rect = new RectF();
        Drawable d = getDrawable();
        if (null != d)
        {
            rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            matrix.mapRect(rect); //If this is not this, the output of the following Log will be the same as the previous sentence.
        }
        Log.e("aaaa",""+rect.bottom+"  "+rect.left+"   "+rect.right+"  "+rect.top);
        return rect;
    }

    @Override
    public void onGlobalLayout() {
        if (once)
        {
            Drawable d = getDrawable();
            if (d == null)
                return;
            Log.e("TAG", d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());
            int width = getWidth();
            int height = getHeight();
            //  Get the width and high of the picture
            int dw = d.getIntrinsicWidth();
            int dh = d.getIntrinsicHeight();
            float scale = 1.0f;
            // If the image is wide or higher than the screen, zoom to the width or high of the screen.
            if (dw > width && dh <= height)
            {
                scale = width * 1.0f / dw;
            }
            if (dh > height && dw <= width)
            {
                scale = height * 1.0f / dh;
            }
            //  If the width and high are greater than the screen, it will make it adapt to the screen size according to the proportion.
            if (dw > width && dh > height)
            {
                scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
            }
            initScale = scale;

            Log.e("TAG", "initScale = " + initScale);
            matrix.postTranslate((width - dw) / 2, (height - dh) / 2);
            matrix.postScale(scale, scale, getWidth() / 2,
                    getHeight() / 2);
            //  Image Move to the screen center
            setImageMatrix(matrix);

            oldMatrix.set(getImageMatrix());
            once = false;

            RectF rectF=getMatrixRectF();
            setDoubleClickScale(rectF);

        }
    }
}

Also, I used the below ExtendedViewPager:

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.viewpager.widget.ViewPager;

public class ExtendedViewPager extends ViewPager {

    public ExtendedViewPager(Context context) {
        super(context);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        try {
            return super.onTouchEvent(ev);
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        try {
            return super.onInterceptTouchEvent(ev);
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
        return false;
    }


}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top