Android 4.3 ImageView 与 ScaleType: MATRIX

Posted

技术标签:

【中文标题】Android 4.3 ImageView 与 ScaleType: MATRIX【英文标题】:Android 4.3 ImageView with ScaleType: MATRIX 【发布时间】:2013-08-03 19:06:40 【问题描述】:

我的问题最类似于:

android 4.3 ImageView ScaleType.MATRIX

还有回声:

android zoom image using matrix

但是,使用这些答案的解决方案解决了我的问题。

我正在使用 Mike Ortiz 的 TouchImageView 的修改版本,以允许双击和常规缩放手势来缩放我的 ImageView。对于Android 4.2.2及更低版本,注释掉“setScaleType(ScaleType.MATRIX)”就可以了,图片在ImageView中居中,可以随意放大缩小。但是,取消注释会导致 ImageView 的图像大小不正确。这一切都很好,因为我刚刚留下那行评论,一切正常。

但是,从 Android 4.3 开始,您无法在没有 setScaleType(ScaleType.MATRIX) 的情况下进行缩放。所以我不得不取消注释该行,但我从未解决的老问题又回来了。

那么,首先,当我所做的只是设置 ScaleType 时,为什么我的计算无法产生正确的图像/ImageView 比率?其次,为什么它会在 4.2.2 上放大而不设置比例类型,但在 4.3 上却不会?最后,有人可以给我一些资源来了解更多关于 ScaleType.MATRIX 的真正作用吗?我已经阅读了一些资料,并进行了一些谷歌搜索,但并没有完全掌握。

提前谢谢你,代码如下。

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;
    


【问题讨论】:

你找到答案了吗 @TerrilThomas 是的,我做到了。我将图像文件放在 res > drawable 中,而不是 res > drawable-nodpi。结果,android 不知道不自动缩放它,所以虽然我所有的计算都是正确的,但它又被缩放了,这把事情搞砸了。我将创建一个答案,并在下周左右接受它。 【参考方案1】:

事实证明,这个问题与 MATRIX 缩放无关,尽管这对我来说仍然是个谜。问题是“自动”重新缩放。我已将图像放在“可绘制”文件夹中,Android 会自动缩放以“最适合屏幕尺寸”。有关这方面的更多信息,请参见此处:

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

解决方案是将图像放在 drawable-nodpi 中。这告诉 Android 不要根据屏幕大小重新缩放,因此,手动操作缩放不会与 Android 自己的缩放冲突。

【讨论】:

以上是关于Android 4.3 ImageView 与 ScaleType: MATRIX的主要内容,如果未能解决你的问题,请参考以下文章

Imageview属性

ANDROID_MARS学习笔记_S01_006ImageView

?attr/selectableItemBackgroundBorderless 在工具栏 Android Studio 内的 ImageView 中不起作用

Android课程---Android ImageView的scaleType属性与adjustViewBounds属性(转)

Android——ImageView的scaleType属性与adjustViewBounds属性 (转)二

Android——ImageView的scaleType属性与adjustViewBounds属性 (转)一