Установка границы изображения при перетаскивании / прокрутке с помощью MultiTouchListener

#java #android #touch #multi-touch

Вопрос:

Я использовал multitouchListener с OnTouchListener, который может позволить мне масштабировать и перетаскивать увеличенное изображение, но изображение выходит за пределы экрана, как я могу это остановить. Вот ссылка для GesturesLitener, которую я использовал https://www.javatips.net/api/PradaCollage-master/prada-collage-app/src/com/thuytrinh/multitouchlistener/MultiTouchListener.java

Вот несколько скриншотов, которые я реализовал до сих пор

1. во-первых, это мой фактический imageview, который имеет изображение.

2. второе — увеличенное изображение.

3. третье — это мое изображение маски, которое я устанавливаю поверх этого изображения с помощью растрового изображения с помощью (x, y, width, height).

4. вот как я могу установить изображение, и изображение исчезает с экрана.

Еще один образец изображения

MultiTouchListener.java

   package com.thuytrinh.multitouchlistener;
  import android.view.GestureDetector;
  import android.view.GestureDetector.OnGestureListener;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.View.OnTouchListener;

   public class MultiTouchListener implements OnTouchListener {

      private class ScaleGestureListener extends 
   ScaleGestureDetector.SimpleOnScaleGestureListener {

    private float mPivotX;
    private float mPivotY;
    private Vector2D mPrevSpanVector = new Vector2D();

    @Override
    public boolean onScaleBegin(View view, ScaleGestureDetector detector) {
        mPivotX = detector.getFocusX();
        mPivotY = detector.getFocusY();
        mPrevSpanVector.set(detector.getCurrentSpanVector());
        return true;
    }

    @Override
    public boolean onScale(View view, ScaleGestureDetector detector) {
        TransformInfo info = new TransformInfo();
        info.deltaScale = isScaleEnabled ? detector.getScaleFactor() : 1.0f;
        info.deltaAngle = isRotateEnabled ? Vector2D.getAngle(mPrevSpanVector, detector.getCurrentSpanVector()) : 0.0f;
        info.deltaX = isTranslateEnabled ? detector.getFocusX() - mPivotX : 0.0f;
        info.deltaY = isTranslateEnabled ? detector.getFocusY() - mPivotY : 0.0f;
        info.pivotX = mPivotX;
        info.pivotY = mPivotY;
        info.minimumScale = minimumScale;
        info.maximumScale = maximumScale;

        move(view, info);
        return false;
    }
}

private class TransformInfo {

    public float deltaX;
    public float deltaY;
    public float deltaScale;
    public float deltaAngle;
    public float pivotX;
    public float pivotY;
    public float minimumScale;
    public float maximumScale;
}

private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private float mPrevX;
private float mPrevY;
private ScaleGestureDetector mScaleGestureDetector;

public boolean isRotateEnabled = true;
public boolean isTranslateEnabled = true;
public boolean isScaleEnabled = true;
public float minimumScale = 0.5f;
public float maximumScale = 10.0f;
private GestureDetector mGestureDetector;

@SuppressWarnings("deprecation")
public MultiTouchListener() {
    mScaleGestureDetector = new ScaleGestureDetector(new ScaleGestureListener());
}

public MultiTouchListener(GestureDetector.SimpleOnGestureListener listener) {
    mScaleGestureDetector = new ScaleGestureDetector(new ScaleGestureListener());
    mGestureDetector = new GestureDetector(listener);
}


@Override
public boolean onTouch(View view, MotionEvent event) {
    mScaleGestureDetector.onTouchEvent(view, event);

    if (!isTranslateEnabled) {
        return true;
    }

    int action = event.getAction();
    switch (action amp; event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN: {
            mPrevX = event.getX();
            mPrevY = event.getY();

            // Save the ID of this pointer.
            mActivePointerId = event.getPointerId(0);
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            // Find the index of the active pointer and fetch its position.
            int pointerIndex = event.findPointerIndex(mActivePointerId);
            if (pointerIndex != -1) {
                float currX = event.getX(pointerIndex);
                float currY = event.getY(pointerIndex);

                // Only move if the ScaleGestureDetector isn't processing a
                // gesture.
                if (!mScaleGestureDetector.isInProgress()) {
                    adjustTranslation(view, currX - mPrevX, currY - mPrevY);
                }
            }

            break;
        }

        case MotionEvent.ACTION_CANCEL:
            mActivePointerId = INVALID_POINTER_ID;
            break;

        case MotionEvent.ACTION_UP:
            mActivePointerId = INVALID_POINTER_ID;
            break;

        case MotionEvent.ACTION_POINTER_UP: {
            // Extract the index of the pointer that left the touch sensor.
            int pointerIndex = (action amp; MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            int pointerId = event.getPointerId(pointerIndex);
            if (pointerId == mActivePointerId) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mPrevX = event.getX(newPointerIndex);
                mPrevY = event.getY(newPointerIndex);
                mActivePointerId = event.getPointerId(newPointerIndex);
            }

            break;
        }
    }
    if(mGestureDetector!=null)
        mGestureDetector.onTouchEvent(event);
    
    return true;
}

private static float adjustAngle(float degrees) {
    if (degrees > 180.0f) {
        degrees -= 360.0f;
    } else if (degrees < -180.0f) {
        degrees  = 360.0f;
    }

    return degrees;
}

private static void move(View view, TransformInfo info) {
    computeRenderOffset(view, info.pivotX, info.pivotY);
    adjustTranslation(view, info.deltaX, info.deltaY);

    // Assume that scaling still maintains aspect ratio.
    float scale = view.getScaleX() * info.deltaScale;
    scale = Math.max(info.minimumScale, Math.min(info.maximumScale, scale));
    view.setScaleX(scale);
    view.setScaleY(scale);

    float rotation = adjustAngle(view.getRotation()   info.deltaAngle);
    view.setRotation(rotation);
}

private static void adjustTranslation(View view, float deltaX, float deltaY) {
    float[] deltaVector = { deltaX, deltaY };
    view.getMatrix().mapVectors(deltaVector);
    view.setTranslationX(view.getTranslationX()   deltaVector[0]);
    view.setTranslationY(view.getTranslationY()   deltaVector[1]);
}

private static void computeRenderOffset(View view, float pivotX, float pivotY) {
    if (view.getPivotX() == pivotX amp;amp; view.getPivotY() == pivotY) {
        return;
    }

    float[] prevPoint = { 0.0f, 0.0f };
    view.getMatrix().mapPoints(prevPoint);

    view.setPivotX(pivotX);
    view.setPivotY(pivotY);

    float[] currPoint = { 0.0f, 0.0f };
    view.getMatrix().mapPoints(currPoint);

    float offsetX = currPoint[0] - prevPoint[0];
    float offsetY = currPoint[1] - prevPoint[1];

    view.setTranslationX(view.getTranslationX() - offsetX);
    view.setTranslationY(view.getTranslationY() - offsetY);
}}
 

ScaleGestureDetector.java

 import android.util.Log;
import android.view.MotionEvent;
import android.view.View;


public class ScaleGestureDetector {
private static final String TAG = "ScaleGestureDetector";


public interface OnScaleGestureListener {

    public boolean onScale(View view, ScaleGestureDetector detector);


    public boolean onScaleBegin(View view, ScaleGestureDetector detector);


    public void onScaleEnd(View view, ScaleGestureDetector detector);
}


public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {

    public boolean onScale(View view, ScaleGestureDetector detector) {
        return false;
    }

    public boolean onScaleBegin(View view, ScaleGestureDetector detector) {
        return true;
    }

    public void onScaleEnd(View view, ScaleGestureDetector detector) {
    }
}


private static final float PRESSURE_THRESHOLD = 0.67f;

private final OnScaleGestureListener mListener;
private boolean mGestureInProgress;

private MotionEvent mPrevEvent;
private MotionEvent mCurrEvent;

private Vector2D mCurrSpanVector;
private float mFocusX;
private float mFocusY;
private float mPrevFingerDiffX;
private float mPrevFingerDiffY;
private float mCurrFingerDiffX;
private float mCurrFingerDiffY;
private float mCurrLen;
private float mPrevLen;
private float mScaleFactor;
private float mCurrPressure;
private float mPrevPressure;
private long mTimeDelta;

private boolean mInvalidGesture;


private int mActiveId0;
private int mActiveId1;
private boolean mActive0MostRecent;

public ScaleGestureDetector(OnScaleGestureListener listener) {
    mListener = listener;
    mCurrSpanVector = new Vector2D();
}

public boolean onTouchEvent(View view, MotionEvent event) {
    final int action = event.getActionMasked();
    if (action == MotionEvent.ACTION_DOWN) {
        reset();
    }
    boolean handled = true;
    if (mInvalidGesture) {
        handled = false;
    } else if (!mGestureInProgress) {
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                mActiveId0 = event.getPointerId(0);
                mActive0MostRecent = true;
            }
            break;
            case MotionEvent.ACTION_UP:
                reset();
                break;
            case MotionEvent.ACTION_POINTER_DOWN: {
                if (mPrevEvent != null) mPrevEvent.recycle();
                mPrevEvent = MotionEvent.obtain(event);
                mTimeDelta = 0;
                int index1 = event.getActionIndex();
                int index0 = event.findPointerIndex(mActiveId0);
                mActiveId1 = event.getPointerId(index1);
                if (index0 < 0 || index0 == index1) {
                    index0 = findNewActiveIndex(event, mActiveId1, -1);
                    mActiveId0 = event.getPointerId(index0);
                }
                mActive0MostRecent = false;
                setContext(view, event);
                mGestureInProgress = mListener.onScaleBegin(view, this);
                break;
            }
        }
    } else {
        switch (action) {
            case MotionEvent.ACTION_POINTER_DOWN: {
                mListener.onScaleEnd(view, this);
                final int oldActive0 = mActiveId0;
                final int oldActive1 = mActiveId1;
                reset();
                mPrevEvent = MotionEvent.obtain(event);
                mActiveId0 = mActive0MostRecent ? oldActive0 : oldActive1;
                mActiveId1 = event.getPointerId(event.getActionIndex());
                mActive0MostRecent = false;
                int index0 = event.findPointerIndex(mActiveId0);
                if (index0 < 0 || mActiveId0 == mActiveId1) {
                    index0 = findNewActiveIndex(event, mActiveId1, -1);
                    mActiveId0 = event.getPointerId(index0);
                }
                setContext(view, event);
                mGestureInProgress = mListener.onScaleBegin(view, this);
            }
            break;
            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerCount = event.getPointerCount();
                final int actionIndex = event.getActionIndex();
                final int actionId = event.getPointerId(actionIndex);
                boolean gestureEnded = false;
                if (pointerCount > 2) {
                    if (actionId == mActiveId0) {
                        final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex);
                        if (newIndex >= 0) {
                            mListener.onScaleEnd(view, this);
                            mActiveId0 = event.getPointerId(newIndex);
                            mActive0MostRecent = true;
                            mPrevEvent = MotionEvent.obtain(event);
                            setContext(view, event);
                            mGestureInProgress = mListener.onScaleBegin(view, this);
                        } else {
                            gestureEnded = true;
                        }
                    } else if (actionId == mActiveId1) {
                        final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex);
                        if (newIndex >= 0) {
                            mListener.onScaleEnd(view, this);
                            mActiveId1 = event.getPointerId(newIndex);
                            mActive0MostRecent = false;
                            mPrevEvent = MotionEvent.obtain(event);
                            setContext(view, event);
                            mGestureInProgress = mListener.onScaleBegin(view, this);
                        } else {
                            gestureEnded = true;
                        }
                    }
                    mPrevEvent.recycle();
                    mPrevEvent = MotionEvent.obtain(event);
                    setContext(view, event);
                } else {
                    gestureEnded = true;
                }
                if (gestureEnded) {
                    setContext(view, event);
                    final int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0;
                    final int index = event.findPointerIndex(activeId);
                    mFocusX = event.getX(index);
                    mFocusY = event.getY(index);
                    mListener.onScaleEnd(view, this);
                    reset();
                    mActiveId0 = activeId;
                    mActive0MostRecent = true;
                }
            }
            break;
            case MotionEvent.ACTION_CANCEL:
                mListener.onScaleEnd(view, this);
                reset();
                break;
            case MotionEvent.ACTION_UP:
                reset();
                break;
            case MotionEvent.ACTION_MOVE: {
                setContext(view, event);
                if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
                    final boolean updatePrevious = mListener.onScale(view, this);
                    if (updatePrevious) {
                        mPrevEvent.recycle();
                        mPrevEvent = MotionEvent.obtain(event);
                    }
                }
            }
            break;
        }
    }
    return handled;
}

private int findNewActiveIndex(MotionEvent ev, int otherActiveId, int removedPointerIndex) {
    final int pointerCount = ev.getPointerCount();
    final int otherActiveIndex = ev.findPointerIndex(otherActiveId);
    for (int i = 0; i < pointerCount; i  ) {
        if (i != removedPointerIndex amp;amp; i != otherActiveIndex) {
            return i;
        }
    }
    return -1;
}

private void setContext(View view, MotionEvent curr) {
    if (mCurrEvent != null) {
        mCurrEvent.recycle();
    }
    mCurrEvent = MotionEvent.obtain(curr);
    mCurrLen = -1;
    mPrevLen = -1;
    mScaleFactor = -1;
    mCurrSpanVector.set(0.0f, 0.0f);
    final MotionEvent prev = mPrevEvent;
    final int prevIndex0 = prev.findPointerIndex(mActiveId0);
    final int prevIndex1 = prev.findPointerIndex(mActiveId1);
    final int currIndex0 = curr.findPointerIndex(mActiveId0);
    final int currIndex1 = curr.findPointerIndex(mActiveId1);
    if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0) {
        mInvalidGesture = true;
        Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable());
        if (mGestureInProgress) {
            mListener.onScaleEnd(view, this);
        }
        return;
    }
    final float px0 = prev.getX(prevIndex0);
    final float py0 = prev.getY(prevIndex0);
    final float px1 = prev.getX(prevIndex1);
    final float py1 = prev.getY(prevIndex1);
    final float cx0 = curr.getX(currIndex0);
    final float cy0 = curr.getY(currIndex0);
    final float cx1 = curr.getX(currIndex1);
    final float cy1 = curr.getY(currIndex1);
    final float pvx = px1 - px0;
    final float pvy = py1 - py0;
    final float cvx = cx1 - cx0;
    final float cvy = cy1 - cy0;
    mCurrSpanVector.set(cvx, cvy);
    mPrevFingerDiffX = pvx;
    mPrevFingerDiffY = pvy;
    mCurrFingerDiffX = cvx;
    mCurrFingerDiffY = cvy;
    mFocusX = cx0   cvx * 0.5f;
    mFocusY = cy0   cvy * 0.5f;
    mTimeDelta = curr.getEventTime() - prev.getEventTime();
    mCurrPressure = curr.getPressure(currIndex0)   curr.getPressure(currIndex1);
    mPrevPressure = prev.getPressure(prevIndex0)   prev.getPressure(prevIndex1);
}

private void reset() {
    if (mPrevEvent != null) {
        mPrevEvent.recycle();
        mPrevEvent = null;
    }
    if (mCurrEvent != null) {
        mCurrEvent.recycle();
        mCurrEvent = null;
    }
    mGestureInProgress = false;
    mActiveId0 = -1;
    mActiveId1 = -1;
    mInvalidGesture = false;
}


public boolean isInProgress() {
    return mGestureInProgress;
}


public float getFocusX() {
    return mFocusX;
}


public float getFocusY() {
    return mFocusY;
}


public float getCurrentSpan() {
    if (mCurrLen == -1) {
        final float cvx = mCurrFingerDiffX;
        final float cvy = mCurrFingerDiffY;
        mCurrLen = (float) Math.sqrt(cvx * cvx   cvy * cvy);
    }
    return mCurrLen;
}

public Vector2D getCurrentSpanVector() {
    return mCurrSpanVector;
}


public float getCurrentSpanX() {
    return mCurrFingerDiffX;
}


public float getCurrentSpanY() {
    return mCurrFingerDiffY;
}


public float getPreviousSpan() {
    if (mPrevLen == -1) {
        final float pvx = mPrevFingerDiffX;
        final float pvy = mPrevFingerDiffY;
        mPrevLen = (float) Math.sqrt(pvx * pvx   pvy * pvy);
    }
    return mPrevLen;
}


public float getPreviousSpanX() {
    return mPrevFingerDiffX;
}


public float getPreviousSpanY() {
    return mPrevFingerDiffY;
}


public float getScaleFactor() {
    if (mScaleFactor == -1) {
        mScaleFactor = getCurrentSpan() / getPreviousSpan();
    }
    return mScaleFactor;
}


public long getTimeDelta() {
    return mTimeDelta;
}


public long getEventTime() {
    return mCurrEvent.getEventTime();
}}
 

Vector2D.java

  package com.thuytrinh.multitouchlistener;

 import android.graphics.PointF;

 public class Vector2D extends PointF {

public Vector2D() {
    super();
}

public Vector2D(float x, float y) {
    super(x, y);
}

public void normalize() {
    float length = (float) Math.sqrt(x * x   y * y);
    x /= length;
    y /= length;
}

public static float getAngle(Vector2D vector1, Vector2D vector2) {
    vector1.normalize();
    vector2.normalize();
    double degrees = (180.0 / Math.PI) * (Math.atan2(vector2.y, vector2.x) - Math.atan2(vector1.y, vector1.x));
    return (float) degrees;
}}