I am using a class TouchImageViewavailable at https://github.com/MikeOrtiz/TouchImageView .
when I move from the enlarged image to the next and return to the previous image, it still has zooming.
I want to reset the image scaling to its normal position when scrolling to the previous image
and got an answer, referring to this question How to reset the image to its original position , but I don’t know exactly where this method should be put in this library so that you can help me with the manual?
Thanks in advance.
public class TouchImageView extends ImageView {
private static final String DEBUG = "DEBUG";
private static final float SUPER_MIN_MULTIPLIER = .75f;
private static final float SUPER_MAX_MULTIPLIER = 1.25f;
private float normalizedScale;
private Matrix matrix, prevMatrix;
private boolean onTouch = false;
private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM };
private State state;
private float minScale;
private float maxScale;
private float superMinScale;
private float superMaxScale;
private float[] m;
private Context context;
private Fling fling;
private ScaleType mScaleType;
private boolean onDrawReady;
private ZoomVariables delayedZoomVariables;
private int viewWidth, viewHeight, prevViewWidth, prevViewHeight;
private float matchViewWidth, matchViewHeight, prevMatchViewWidth, prevMatchViewHeight;
private ScaleGestureDetector mScaleDetector;
private GestureDetector mGestureDetector;
public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
sharedConstructing(context);
}
OnScaleChangeListener _scaleChangeListener;
public interface OnScaleChangeListener {
public void onSingleTabfromTouchImage(boolean isSingleTabClick);
public void onDoubleTapfromTouchImage(boolean isDoubleTapClick);
}
public synchronized void setOnScaleChangeListener(OnScaleChangeListener listener){
_scaleChangeListener = listener;
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mGestureDetector = new GestureDetector(context, new GestureListener());
matrix = new Matrix();
prevMatrix = new Matrix();
m = new float[9];
normalizedScale = 1;
if (mScaleType == null) {
mScaleType = ScaleType.FIT_CENTER;
}
minScale = 1;
maxScale = 3;
superMinScale = SUPER_MIN_MULTIPLIER * minScale;
superMaxScale = SUPER_MAX_MULTIPLIER * maxScale;
setImageMatrix(matrix);
setScaleType(ScaleType.MATRIX);
setState(State.NONE);
setOnTouchListener(new TouchImageViewListener());
onDrawReady = false;
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
savePreviousImageValues();
fitImageToView();
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
savePreviousImageValues();
fitImageToView();
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
savePreviousImageValues();
fitImageToView();
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
savePreviousImageValues();
fitImageToView();
}
@Override
public void setScaleType(ScaleType type) {
if (type == ScaleType.FIT_START || type == ScaleType.FIT_END) {
throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END");
}
if (type == ScaleType.MATRIX) {
super.setScaleType(ScaleType.MATRIX);
} else {
mScaleType = type;
}
}
@Override
public ScaleType getScaleType() {
return mScaleType;
}
public boolean isZoomed() {
if(normalizedScale > 1)
return true;
else
return false;
}
public Bitmap getZoomedImage() {
Bitmap bitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layout(0, 0, viewWidth, viewHeight);
draw(canvas);
return bitmap;
}
public Bitmap getZoomedImageFromSource() {
if (mScaleType == ScaleType.FIT_XY) {
throw new UnsupportedOperationException("getZoomedImageFromSource() not supported with FIT_XY");
}
Bitmap bitmap = ((BitmapDrawable) getDrawable()).getBitmap();
Rect r = getZoomedRect();
if (r.width() <= 0 || r.height() <= 0) {
return null;
}
return Bitmap.createBitmap(bitmap, r.left, r.top, r.width(), r.height());
}
public Rect getZoomedRect() {
if (mScaleType == ScaleType.FIT_XY) {
throw new UnsupportedOperationException("getZoomedRect() not supported with FIT_XY");
}
PointF topLeft = getDrawablePointFromTouchPoint(0,0);
PointF bottomRight = getDrawablePointFromTouchPoint(viewWidth, viewHeight);
return new Rect((int) topLeft.x, (int) topLeft.y,(int) bottomRight.x, (int) bottomRight.y);
}
private void savePreviousImageValues() {
if (matrix != null) {
matrix.getValues(m);
prevMatrix.setValues(m);
prevMatchViewHeight = matchViewHeight;
prevMatchViewWidth = matchViewWidth;
prevViewHeight = viewHeight;
prevViewWidth = viewWidth;
}
}
@Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable("instanceState", super.onSaveInstanceState());
bundle.putFloat("saveScale", normalizedScale);
bundle.putFloat("matchViewHeight", matchViewHeight);
bundle.putFloat("matchViewWidth", matchViewWidth);
bundle.putInt("viewWidth", viewWidth);
bundle.putInt("viewHeight", viewHeight);
matrix.getValues(m);
bundle.putFloatArray("matrix", m);
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
normalizedScale = bundle.getFloat("saveScale");
m = bundle.getFloatArray("matrix");
prevMatrix.setValues(m);
prevMatchViewHeight = bundle.getFloat("matchViewHeight");
prevMatchViewWidth = bundle.getFloat("matchViewWidth");
prevViewHeight = bundle.getInt("viewHeight");
prevViewWidth = bundle.getInt("viewWidth");
super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
return;
}
super.onRestoreInstanceState(state);
}
@Override
protected void onDraw(Canvas canvas) {
onDrawReady = true;
if (delayedZoomVariables != null) {
setZoom(delayedZoomVariables.scale, delayedZoomVariables.focusX, delayedZoomVariables.focusY, delayedZoomVariables.scaleType);
delayedZoomVariables = null;
}
super.onDraw(canvas);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
resetZoom();
}
public float getMaxZoom() {
return maxScale;
}
public void setMaxZoom(float max) {
maxScale = max;
superMaxScale = SUPER_MAX_MULTIPLIER * maxScale;
}
public float getMinZoom() {
return minScale;
}
public float getCurrentZoom() {
return normalizedScale;
}
public void setMinZoom(float min) {
minScale = min;
superMinScale = SUPER_MIN_MULTIPLIER * minScale;
}
public void resetZoom() {
normalizedScale = 1;
fitImageToView();
}
public void setZoom(float scale) {
setZoom(scale, 0.5f, 0.5f);
}
public void setZoom(float scale, float focusX, float focusY) {
setZoom(scale, focusX, focusY, mScaleType);
}
public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) {
if (!onDrawReady) {
delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType);
return;
}
setScaleType(scaleType);
resetZoom();
scaleImage(scale, viewWidth / 2, viewHeight / 2, false);
matrix.getValues(m);
m[Matrix.MTRANS_X] = -((focusX * getImageWidth()) - (viewWidth * 0.5f));
m[Matrix.MTRANS_Y] = -((focusY * getImageHeight()) - (viewHeight * 0.5f));
matrix.setValues(m);
fixTrans();
setImageMatrix(matrix);
}
public void setZoom(TouchImageView img) {
PointF center = img.getCenterOfZoomedImage();
setZoom(img.getCurrentZoom(), center.x, center.y, img.getScaleType());
}
public PointF getDrawablePointFromTouchPoint(float x, float y) {
return transformCoordTouchToBitmap(x, y, true);
}
public PointF getDrawablePointFromTouchPoint(PointF p) {
return transformCoordTouchToBitmap(p.x, p.y, true);
}
public PointF getCenterOfZoomedImage() {
Drawable drawable = getDrawable();
if (drawable == null) {
return null;
}
int drawableWidth = drawable.getIntrinsicWidth();
int drawableHeight = drawable.getIntrinsicHeight();
PointF point = getDrawablePointFromTouchPoint(viewWidth / 2, viewHeight / 2);
point.x /= drawableWidth;
point.y /= drawableHeight;
return point;
}
private void fixTrans() {
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];
float fixTransX = getFixTrans(transX, viewWidth, getImageWidth());
float fixTransY = getFixTrans(transY, viewHeight, getImageHeight());
if (fixTransX != 0 || fixTransY != 0) {
matrix.postTranslate(fixTransX, fixTransY);
}
}
private void fixScaleTrans() {
fixTrans();
matrix.getValues(m);
if (getImageWidth() < viewWidth) {
m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2;
}
if (getImageHeight() < viewHeight) {
m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2;
}
matrix.setValues(m);
}
private float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;
if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}
if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}
private float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
private float getImageWidth() {
return matchViewWidth * normalizedScale;
}
private float getImageHeight() {
return matchViewHeight * normalizedScale;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
setMeasuredDimension(0, 0);
return;
}
int drawableWidth = drawable.getIntrinsicWidth();
int drawableHeight = drawable.getIntrinsicHeight();
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
viewWidth = setViewSize(widthMode, widthSize, drawableWidth);
viewHeight = setViewSize(heightMode, heightSize, drawableHeight);
setMeasuredDimension(viewWidth, viewHeight);
fitImageToView();
}
private void fitImageToView() {
Drawable drawable = getDrawable();
if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
return;
}
if (matrix == null || prevMatrix == null) {
return;
}
int drawableWidth = drawable.getIntrinsicWidth();
int drawableHeight = drawable.getIntrinsicHeight();
float scaleX = (float) viewWidth / drawableWidth;
float scaleY = (float) viewHeight / drawableHeight;
switch (mScaleType) {
case CENTER:
scaleX = scaleY = 1;
break;
case CENTER_CROP:
scaleX = scaleY = Math.max(scaleX, scaleY);
break;
case CENTER_INSIDE:
scaleX = scaleY = Math.min(1, Math.min(scaleX, scaleY));
case FIT_CENTER:
scaleX = scaleY = Math.min(scaleX, scaleY);
break;
case FIT_XY:
break;
default:
throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END");
}
float redundantXSpace = viewWidth - (scaleX * drawableWidth);
float redundantYSpace = viewHeight - (scaleY * drawableHeight);
matchViewWidth = viewWidth - redundantXSpace;
matchViewHeight = viewHeight - redundantYSpace;
if (!isZoomed()) {
matrix.setScale(scaleX, scaleY);
matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2);
normalizedScale = 1;
} else {
if(onTouch){
if(matchViewWidth > drawableWidth){
prevMatchViewHeight = 0;
prevMatchViewWidth = 0;
savePreviousImageValues();
}
onTouch = false;
}
if (prevMatchViewWidth == 0 || prevMatchViewHeight == 0) {
normalizedScale = 1;
}
prevMatrix.getValues(m);
m[Matrix.MSCALE_X] = matchViewWidth / drawableWidth * normalizedScale;
m[Matrix.MSCALE_Y] = matchViewHeight / drawableHeight * normalizedScale;
float transX = m[Matrix.MTRANS_X];
float transY = m[Matrix.MTRANS_Y];
float prevActualWidth = prevMatchViewWidth * normalizedScale;
float actualWidth = getImageWidth();
translateMatrixAfterRotate(Matrix.MTRANS_X, transX, prevActualWidth, actualWidth, prevViewWidth, viewWidth, drawableWidth);
float prevActualHeight = prevMatchViewHeight * normalizedScale;
float actualHeight = getImageHeight();
translateMatrixAfterRotate(Matrix.MTRANS_Y, transY, prevActualHeight, actualHeight, prevViewHeight, viewHeight, drawableHeight);
matrix.setValues(m);
}
fixTrans();
setImageMatrix(matrix);
}
private int setViewSize(int mode, int size, int drawableWidth) {
int viewSize;
switch (mode) {
case MeasureSpec.EXACTLY:
viewSize = size;
break;
case MeasureSpec.AT_MOST:
viewSize = Math.min(drawableWidth, size);
break;
case MeasureSpec.UNSPECIFIED:
viewSize = drawableWidth;
break;
default:
viewSize = size;
break;
}
return viewSize;
}
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapConfirmed(MotionEvent e)
{
onTouch = true;
_scaleChangeListener.onSingleTabfromTouchImage(true);
savePreviousImageValues();
return false;
}
@Override
public void onLongPress(MotionEvent e)
{
performLongClick();
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
if (fling != null) {
fling.cancelFling();
}
fling = new Fling((int) velocityX, (int) velocityY);
compatPostOnAnimation(fling);
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public boolean onDoubleTap(MotionEvent e) {
boolean consumed = false;
if (state == State.NONE) {
float targetZoom = (normalizedScale == minScale) ? maxScale : minScale;
DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, e.getX(), e.getY(), false);
compatPostOnAnimation(doubleTap);
consumed = true;
}
return consumed;
}
}
private class TouchImageViewListener implements OnTouchListener {
private PointF last = new PointF();
@Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
mGestureDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());
if (state == State.NONE || state == State.DRAG || state == State.FLING) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(curr);
if (fling != null)
fling.cancelFling();
setState(State.DRAG);
break;
case MotionEvent.ACTION_MOVE:
if (state == State.DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth, getImageWidth());
float fixTransY = getFixDragTrans(deltaY, viewHeight, getImageHeight());
matrix.postTranslate(fixTransX, fixTransY);
fixTrans();
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
setState(State.NONE);
break;
}
}
setImageMatrix(matrix);
return true;
}
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
setState(State.ZOOM);
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true);
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
super.onScaleEnd(detector);
setState(State.NONE);
boolean animateToZoomBoundary = false;
float targetZoom = normalizedScale;
if (normalizedScale > maxScale) {
targetZoom = maxScale;
animateToZoomBoundary = true;
} else if (normalizedScale < minScale) {
targetZoom = minScale;
animateToZoomBoundary = true;
}
if (animateToZoomBoundary) {
DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, viewWidth / 2, viewHeight / 2, true);
compatPostOnAnimation(doubleTap);
}
}
}
private void scaleImage(float deltaScale, float focusX, float focusY, boolean stretchImageToSuper) {
float lowerScale, upperScale;
if (stretchImageToSuper) {
lowerScale = superMinScale;
upperScale = superMaxScale;
} else {
lowerScale = minScale;
upperScale = maxScale;
}
float origScale = normalizedScale;
normalizedScale *= deltaScale;
if (normalizedScale > upperScale) {
normalizedScale = upperScale;
deltaScale = upperScale / origScale;
} else if (normalizedScale < lowerScale) {
normalizedScale = lowerScale;
deltaScale = lowerScale / origScale;
}
matrix.postScale(deltaScale, deltaScale, focusX, focusY);
fixScaleTrans();
}