My question is most similar to:
Android 4.3 ImageView ScaleType.MATRIX
but also echos:
android zoom image using matrix
However, using the solutions to those answers has solved my problem.
I am using a modified version of Mike Ortiz's TouchImageView to allow for both double tapping and the regular scale gesture to zoom my ImageView. For Android 4.2.2 and lower, commenting out the "setScaleType(ScaleType.MATRIX)" was just fine, the image was centered in the ImageView, and you could zoom in and out at will. Uncommenting, though, would cause the image to be sized incorrectly for the ImageView. All this was just fine, as I just left that line commented, and everything worked.
As of Android 4.3, however, you can't zoom without setScaleType(ScaleType.MATRIX). So I've had to uncomment that line, but the old problem I never solved is back.
So, first and foremost, why do my calculations fail to result in the correct image/ImageView ratio when all I'm doing is setting the ScaleType? Second, why would it zoom just fine on 4.2.2 without setting the scale type, but on 4.3, it won't? And finally, could someone give me some resources to learn more about what ScaleType.MATRIX really does? I've read through some of the sources, and done some googling, but don't totally grasp it.
Thank you in advance, and code below.
public class TouchImageView extends ImageView {
Matrix matrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF last = new PointF();
PointF start = new PointF();
float minScale = 1f;
float maxScale = 10f;
float[] floatArray;
float unusedWidth, unusedHeight;
float imageViewWidth, imageViewHeight;
float initialScale = 1f;
float right, bottom, origWidth, origHeight, imageWidth, imageHeight;
boolean zoomedInLastTime = false;
PointF zoomCenter;
ScaleGestureDetector mScaleDetector;
GestureDetector mDetector;
Context context;
public TouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public TouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.context = context;
setScaleType(ScaleType.MATRIX);
//matrix = this.getImageMatrix();
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
//this is an empty GestureDetector
mDetector = new GestureDetector(this.context, new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return false;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent2, float v, float v2) {
return false;
}
@Override
public void onLongPress(MotionEvent motionEvent) {
}
@Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent2, float v, float v2) {
return false;
}
}, null, true);
mDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener(){
@Override
public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
return false;
}
@Override
public boolean onDoubleTap(MotionEvent motionEvent) {
Log.d("TouchImageView", "double tap heard");
PointF currentTapLocation = new PointF(motionEvent.getX(), motionEvent.getY());
if (!zoomedInLastTime){
(new ScaleListener()).scaleIt(3f,currentTapLocation.x,currentTapLocation.y);
zoomCenter = currentTapLocation;
zoomedInLastTime = true;
}else {
(new ScaleListener()).scaleIt(.33f, zoomCenter.x, zoomCenter.y);
zoomedInLastTime = false;
}
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent motionEvent) {
return false;
}
});
matrix.setTranslate(1f, 1f);
floatArray = new float[9];
setImageMatrix(matrix);
setOnTouchListener(new DoubleTapPinchZoomListener());
}
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
if(bm != null) {
if (Build.VERSION.SDK_INT > 17){
imageWidth = bm.getWidth();
imageHeight = bm.getHeight();
} else {
imageWidth = 2*bm.getWidth();
imageHeight = 2*bm.getHeight();
}
}
}
public void setMaxZoom(float x)
{
maxScale = x;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = ZOOM;
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = detector.getScaleFactor();
scaleIt(mScaleFactor, detector.getFocusX(), detector.getFocusY());
return true;
}
public void scaleIt(float mScaleFactor, float focusx, float focusy){
float origScale = initialScale;
initialScale *= mScaleFactor;
if (initialScale > maxScale) {
initialScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (initialScale < minScale) {
initialScale = minScale;
mScaleFactor = minScale / origScale;
}
right = imageViewWidth * initialScale - imageViewWidth - (2 * unusedWidth * initialScale);
bottom = imageViewHeight * initialScale - imageViewHeight - (2 * unusedHeight * initialScale);
if (origWidth * initialScale <= imageViewWidth || origHeight * initialScale <= imageViewHeight) {
matrix.postScale(mScaleFactor, mScaleFactor, imageViewWidth / 2, imageViewHeight / 2);
if (mScaleFactor < 1) {
matrix.getValues(floatArray);
float x = floatArray[Matrix.MTRANS_X];
float y = floatArray[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (Math.round(origWidth * initialScale) < imageViewWidth) {
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
} else {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
}
}
}
} else {
matrix.postScale(mScaleFactor, mScaleFactor, focusx, focusy);
matrix.getValues(floatArray);
float x = floatArray[Matrix.MTRANS_X];
float y = floatArray[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
}
}
}
}
@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
imageViewWidth = MeasureSpec.getSize(widthMeasureSpec);
imageViewHeight = MeasureSpec.getSize(heightMeasureSpec);
/*RectF drawableRect = new RectF(0, 0, imageWidth, imageHeight);
RectF viewRect = new RectF(0, 0, imageViewWidth, imageViewHeight);
//draw the image in the view
matrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER);*/
//Fit to screen.
float scale;
float scaleX = (float) imageViewWidth / (float)(imageWidth);
float scaleY = (float) imageViewHeight / (float)(imageHeight);
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
setImageMatrix(matrix);
initialScale = 1f;
// Center the image
unusedHeight = (float) imageViewHeight - (scale * (float) imageHeight) ;
unusedWidth = (float) imageViewWidth - (scale * (float) imageWidth);
unusedHeight /= (float)2;
unusedWidth /= (float)2;
matrix.postTranslate(unusedWidth, unusedHeight);
origWidth = imageViewWidth - 2 * unusedWidth;
origHeight = imageViewHeight - 2 * unusedHeight;
right = imageViewWidth * initialScale - imageViewWidth - (2 * unusedWidth * initialScale);
bottom = imageViewHeight * initialScale - imageViewHeight - (2 * unusedHeight * initialScale);
setImageMatrix(matrix);
}
class DoubleTapPinchZoomListener implements OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
mDetector.onTouchEvent(event);
matrix.getValues(floatArray);
float x = floatArray[Matrix.MTRANS_X];
float y = floatArray[Matrix.MTRANS_Y];
PointF curr = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
last.set(event.getX(), event.getY());
start.set(last);
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float scaleWidth = Math.round(origWidth * initialScale);
float scaleHeight = Math.round(origHeight * initialScale);
if (scaleWidth < imageViewWidth) {
deltaX = 0;
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
} else if (scaleHeight < imageViewHeight) {
deltaY = 0;
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
} else {
if (x + deltaX > 0)
deltaX = -x;
else if (x + deltaX < -right)
deltaX = -(x + right);
if (y + deltaY > 0)
deltaY = -y;
else if (y + deltaY < -bottom)
deltaY = -(y + bottom);
}
matrix.postTranslate(deltaX, deltaY);
last.set(curr.x, curr.y);
}
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
}
setImageMatrix(matrix);
invalidate();
return true;
}
}
}