ImageView 的 setImageMatrix 在某些设备上无法正常工作(可能早于 Android 4.4)

ImageView's setImageMatrix doesn't work properly on some devices (maybe older than Android 4.4)

我尝试通过以下代码在触摸事件中移动 ImageView:

public class ScrollableImageView extends ImageView {
    private GestureDetectorCompat gestureDetectorCompat;

    public ScrollableImageView(Context context) {
        this(context, null);
    }

    public ScrollableImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollableImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setScaleType(ScaleType.MATRIX);

        gestureDetectorCompat = new GestureDetectorCompat(context,
                new MySimpleOnGestureListener(this));
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        gestureDetectorCompat.onTouchEvent(event);
        return true;
    }

    public void scroll(float distance) {
        Matrix imageMatrix = getImageMatrix();
        imageMatrix.postTranslate(distance, 0);
        setImageMatrix(imageMatrix);
        invalidate();
    }

    private static class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {
        private ScrollableImageView scrollableImageView;

        public MySimpleOnGestureListener(ScrollableImageView scrollableImageView) {
            this.scrollableImageView = scrollableImageView;
        }

        @Override
        public boolean onDown(MotionEvent e) {
            Utils.log("onDown");
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            scrollableImageView.scroll(-distanceX);
            return true;
        }
    }
}

起初,一切都在 Nexus 5(Android 4.4.4 和 Lollipop)上运行良好。但是后来我尝试了 Android 的旧版本,例如 4.0.4(Galaxy S2) 或 Nexus S(4.1.1)...,其中 none 有效。

然后经过一段时间的努力,我想出了这个解决方案,它在所有设备上都运行良好:

(请注意,现在我通过本地对象跟踪 ImageView 的矩阵对象,而不是通过 ImageView 的 getImageMatrix() 获取它)

public class ScrollableImageView extends ImageView {
    private GestureDetectorCompat gestureDetectorCompat;
    private Matrix imageMatrix;

    public ScrollableImageView(Context context) {
        this(context, null);
    }

    public ScrollableImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollableImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setScaleType(ScaleType.MATRIX);

        imageMatrix = new Matrix();

        gestureDetectorCompat = new GestureDetectorCompat(context,
                new MySimpleOnGestureListener(this));
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        gestureDetectorCompat.onTouchEvent(event);
        return true;
    }

    public void scroll(float distance) {
        imageMatrix.postTranslate(distance, 0);
        setImageMatrix(imageMatrix);
        invalidate();
    }

    private static class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {
        private ScrollableImageView scrollableImageView;

        public MySimpleOnGestureListener(ScrollableImageView scrollableImageView) {
            this.scrollableImageView = scrollableImageView;
        }

        @Override
        public boolean onDown(MotionEvent e) {
            Utils.log("onDown");
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            scrollableImageView.scroll(-distanceX);
            return true;
        }
    }
}

我找到了解决方案,但我仍然不明白为什么我以前的代码不起作用?!

我试图进行研究,但唯一有意义的是 ImageView 的 getImageMatrix() 的文档:

Return the view's optional matrix. This is applied to the view's drawable when it is drawn. If there is no matrix, this method will return an identity matrix. Do not change this matrix in place but make a copy. If you want a different matrix applied to the drawable, be sure to call setImageMatrix().

然后说Do not change this matrix in place but make a copy更让我糊涂了,这样做有什么意义呢?为什么我不能像在以前的代码中那样做? (获取 ImageView 的当前矩阵,然后应用翻译,然后按照文档所述通过 setImageMatrix() 将其设置回去)

有人请给我一些启发,这对我来说太混乱了。

好的,伙计们,我明白了!

实际上文档很有意义。

Return the view's optional matrix. This is applied to the view's drawable when it is drawn. If there is no matrix, this method will return an identity matrix. Do not change this matrix in place but make a copy. If you want a different matrix applied to the drawable, be sure to call setImageMatrix().

由于 ics_mr1 的 ImageView 源代码中的这段代码:

public void setImageMatrix(Matrix matrix) {
    // collaps null and identity to just null
    if (matrix != null && matrix.isIdentity()) {
        matrix = null;
    }

    // don't invalidate unless we're actually changing our matrix
    if (matrix == null && !mMatrix.isIdentity() ||
            matrix != null && !mMatrix.equals(matrix)) {
        mMatrix.set(matrix);
        configureBounds();
        invalidate();
    }
}

所以如果我喜欢:

   Matrix imageMatrix = getImageMatrix();
   imageMatrix.postTranslate(distance, 0);
   setImageMatrix(imageMatrix);

matrix != null && !mMatrix.equals(matrix) 将是 false 因为我重复使用了相同的 Matrix 对象(这就是文档说 Do not change this matrix in place but make a copy 的原因),那么什么都不会发生,因为 none 的条件很满意。

那么现在我想出了一个更优雅的解决方案:

   Matrix imageMatrix = new Matrix(getImageMatrix());
   imageMatrix.postTranslate(distance, 0);
   setImageMatrix(imageMatrix);

就是这样
哎呀