Frage

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;
    }
}
}
War es hilfreich?

Lösung

Turns out the issue had nothing to do with MATRIX scaling, though that does remain somewhat of a mystery to me. The problem was 'automagic' rescaling. I had placed my image in the 'drawable' folder, which Android scales automatically to 'best fit the screen size.' For more information on this, see here:

http://developer.android.com/guide/practices/screens_support.html#support

The solution was to put the image in drawable-nodpi. This tells Android NOT to rescale based on the screen size, and as a result, manipulating the scaling manually doesn't conflict with Android's own scaling.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top