View Pager + ImageView +Pinch Zoom + Rotation
Asked Answered
A

7

24

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.

Antietam answered 6/1, 2014 at 5:52 Comment(0)
T
45

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.

Tobiastobie answered 6/1, 2014 at 6:10 Comment(23)
That workaround didn't work for me..:(. If I scroll with higher velocity it moves me to the next slide.Antietam
@AvtarGuleria Did the other workaround included in my edit help? I know I've successfully employed it in the past.Tobiastobie
No Second workaround too not worked for me. Whenever i scroll horizontally, It just moves me to the second slide. But if I scroll vertically or at some other angle other than horizontal it works..so, Again Horizontally it doesn't work...Antietam
Ok, I will take a look at it again and provide more thorough example code.Tobiastobie
What is the API you are testing on? I know this works with API > 14, though I am having some issues with a gingerbread device.Tobiastobie
@AvtarGuleria The first workaround I linked was correct, it just needed to be adapted to work with TouchImageView. I've included my complete working code in the answer above. Tested on a gingerbread and ICS devices. Let me know if you still have troubles with it.Tobiastobie
Thanks for the updated code, I will try it and let you know. I am providing support for API >= 9. And For your first workaround I have also used with your Github Touch Image view project, But that didn't work for me. But as mentioned in my previous comment your second workaround seems worked but not 100% as explained in my previous comment.Antietam
Im trying to implement your solution, with canScrollHorizontally() or with the solution in the last link on your answer, but it doesnt work. The ViewPager always moves to the next page when horizontally scrolled.Urochrome
The problem is that computeHorizontalScrollRange() and computeHorizontalScrollExtent() are always equal.Urochrome
@Urochrome 1. Make sure you are using code from the dev branch (not master). You can confirm this by seeing that TouchImageView overrides canScrollHorizontally. 2. You can ignore the second link. My example code above is an adaptation of the code in that link for TouchImageView rather than WebView. 3. I and at two other StackOverflow users have confirmed the example code above works. Make sure your implementation exactly follows mine.Tobiastobie
@MikeOrtiz Ok, I took the dev branch version and it works. But now there's a different problem - with the master branch version zooming the image was also increasing the imageview size so that it fills the entire screen. Now when i zoom the image is scaling but the imageview stays the same size. is this configurable ?Urochrome
@MikeOrtiz do you see what im talking about ?Urochrome
@Urochrome No, I'm not seeing your problem. For me, it works as expected. Can you post a bug on github. Could you include all relevant code in separate pastebin links, as well as device and os version? Thanks.Tobiastobie
Posted - https://github.com/MikeOrtiz/TouchImageView/issues/54.Urochrome
@Urochrome Thank you for the response. Your pastebin link is private.Tobiastobie
can someone please help me how to use this code so that i can show multiple images in Pager with zoom in and zoom out functionality using buttons (which is already working) but the only problem is to use ViewPager can someone provide me latest codeDemaggio
This solution didn't work for me either. The zooming in/out is very awkward, most of the time it doesn't do anything and when it does it isn't natural (doesn't follow the amount you moved your fingers.) Also, after resizing an image and going to next slide, after going back the image has disappeared. It's disappointing because I spent all morning porting the code from java to C# Xamarin. :(Dorothadorothea
Here is the Xamarin code in case anyone else wants to play with it further and try to get it working... gist.github.com/justintoth/10663869Dorothadorothea
@Dorothadorothea It sounds like you may be using the code on master which does not yet support view pager and has some other bugs in it. Did you try with the dev branch? Also, I will soon be adding other examples to the sample app including view pager. The examples should be on dev in the next couples days and master soon after.Tobiastobie
@MikeOrtiz can u please paste link here with TouchImageView with ViewPager i need this i have already worked on zoomin and zoomout btn part but the only thing left is to integrate paging with them can u please post touchimageview+pager dev branch link hereDemaggio
Here is a link to an example ViewPager implementation. It is currently on the master branch as of v1.2.0.Tobiastobie
@MikeOrtiz Thanks a lot.+1 for update with view pager.Subtilize
@Mike Ortiz I successfully implement your library but i want few changes and i want that when image is zoom than i restrict the viewpager from swipping or when user press the back key and if image is in zoom state than image should come in default stateTorrietorrin
A
4

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

Awestricken answered 6/8, 2014 at 13:49 Comment(3)
After zooming and swiping the view pager to next image and if I swipe back to previous image, that image not appearing to original positionGarceau
ViewPager doesn't destroy previous image from memory, so when you are moving back ViewPager just shows object with saved state. In order to reset zoom you may use ImageViewTouch's method resetDisplay or any other suitable for you method.Awestricken
yes i used @Master but not worked. #25761005Garceau
F
3

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.

Furmark answered 15/4, 2016 at 18:16 Comment(0)
A
2

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

    }
}
Altar answered 16/2, 2015 at 17:14 Comment(0)
W
0

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

}

Walburga answered 9/3, 2016 at 8:37 Comment(0)
N
0

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;

    }
Nidanidaros answered 17/10, 2019 at 16:30 Comment(0)
L
0

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


}
Lilithe answered 5/1, 2022 at 17:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.