【问题标题】:Android: ImageView click is not get perfect after reset zoomAndroid:重置缩放后ImageView点击不完美
【发布时间】:2015-02-03 05:51:50
【问题描述】:

我有多个基于 X、Y、高度、宽度显示的动态图像。这些图像与 RelativeLayout 绑定,并且此布局与我的 ZoomableViewGroup 类绑定。当我点击图像时,颜色被填充。

public class ZoomableViewGroup extends ViewGroup {

private Context mContext;
private static final int INVALID_POINTER_ID = 1;
private int mActivePointerId = INVALID_POINTER_ID;

private float mScaleFactor = 1;
private ScaleGestureDetector mScaleDetector;
private Matrix mScaleMatrix = new Matrix();
private Matrix mScaleMatrixInverse = new Matrix();

private float mPosX;
private float mPosY;
private Matrix mTranslateMatrix = new Matrix();
private Matrix mTranslateMatrixInverse = new Matrix();

private float mLastTouchX;
private float mLastTouchY;

private float mFocusY;

private float mFocusX;

private float[] mInvalidateWorkingArray = new float[6];
private float[] mDispatchTouchEventWorkingArray = new float[2];
private float[] mOnTouchEventWorkingArray = new float[2];

public ZoomableViewGroup(Context context) {
    super(context);
    this.mContext = context;
    mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    mTranslateMatrix.setTranslate(0, 0);
    mScaleMatrix.setScale(1, 1);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            child.layout(l, t, l + child.getMeasuredWidth(),
                    t + child.getMeasuredHeight());
        }
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
    }
}

@Override
protected void dispatchDraw(Canvas canvas) {
    canvas.save();
    canvas.translate(mPosX, mPosY);
    canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
    super.dispatchDraw(canvas);
    canvas.restore();
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    mDispatchTouchEventWorkingArray[0] = ev.getX();
    mDispatchTouchEventWorkingArray[1] = ev.getY();
    mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
    ev.setLocation(mDispatchTouchEventWorkingArray[0],
            mDispatchTouchEventWorkingArray[1]);
    return super.dispatchTouchEvent(ev);
}

@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    mInvalidateWorkingArray[0] = dirty.left;
    mInvalidateWorkingArray[1] = dirty.top;
    mInvalidateWorkingArray[2] = dirty.right;
    mInvalidateWorkingArray[3] = dirty.bottom;

    mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray);
    dirty.set(Math.round(mInvalidateWorkingArray[0]),
            Math.round(mInvalidateWorkingArray[1]),
            Math.round(mInvalidateWorkingArray[2]),
            Math.round(mInvalidateWorkingArray[3]));

    location[0] *= mScaleFactor;
    location[1] *= mScaleFactor;
    return super.invalidateChildInParent(location, dirty);
}

private float[] scaledPointsToScreenPoints(float[] a) {
    mScaleMatrix.mapPoints(a);
    mTranslateMatrix.mapPoints(a);
    return a;
}

private float[] screenPointsToScaledPoints(float[] a) {
    mTranslateMatrixInverse.mapPoints(a);
    mScaleMatrixInverse.mapPoints(a);
    return a;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    mOnTouchEventWorkingArray[0] = ev.getX();
    mOnTouchEventWorkingArray[1] = ev.getY();

    mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);

    ev.setLocation(mOnTouchEventWorkingArray[0],
            mOnTouchEventWorkingArray[1]);
    mScaleDetector.onTouchEvent(ev);

    final int action = ev.getAction();
    switch (action & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN: {
        final float x = ev.getX();
        final float y = ev.getY();

        mLastTouchX = x;
        mLastTouchY = y;

        // Save the ID of this pointer
        mActivePointerId = ev.getPointerId(0);
        break;
    }

    case MotionEvent.ACTION_MOVE: {
        // Find the index of the active pointer and fetch its position
        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
        final float x = ev.getX(pointerIndex);
        final float y = ev.getY(pointerIndex);

        final float dx = x - mLastTouchX;
        final float dy = y - mLastTouchY;

        mPosX += dx;
        mPosY += dy;
        mTranslateMatrix.preTranslate(dx, dy);
        mTranslateMatrix.invert(mTranslateMatrixInverse);

        mLastTouchX = x;
        mLastTouchY = y;

        invalidate();
        break;
    }

    case MotionEvent.ACTION_UP: {
        mActivePointerId = INVALID_POINTER_ID;
        break;
    }

    case MotionEvent.ACTION_CANCEL: {
        mActivePointerId = INVALID_POINTER_ID;
        break;
    }

    case MotionEvent.ACTION_POINTER_UP: {
        // Extract the index of the pointer that left the touch sensor
        final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
            // This was our active pointer going up. Choose a new
            // active pointer and adjust accordingly.
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastTouchX = ev.getX(newPointerIndex);
            mLastTouchY = ev.getY(newPointerIndex);
            mActivePointerId = ev.getPointerId(newPointerIndex);
        }
        break;
    }
    }
    return true;
}

private class ScaleListener extends
        ScaleGestureDetector.SimpleOnScaleGestureListener {

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        mScaleFactor *= detector.getScaleFactor();
        if (detector.isInProgress()) {
            mFocusX = detector.getFocusX();
            mFocusY = detector.getFocusY();
        }
        mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
        mScaleMatrix.setScale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
        mScaleMatrix.invert(mScaleMatrixInverse);
        invalidate();
        requestLayout();

        return true;
    }
}

public void restoreView() {
    mPosX = 0;
    mPosY = 0;
    mFocusX = 0;
    mFocusY = 0;
    mScaleFactor = 1;
    mTranslateMatrix.setTranslate(0, 0);
    mScaleMatrix.setScale(1, 1);
    invalidate();
}
}

放大/缩小工作完美。使用 restoreView() 功能显示原始状态的视图,它工作正常。但是在恢复到原始状态后,ImageView在View中的点击并没有得到完美的点击。这意味着颜色没有填充整个图像,例如填充一半颜色,一半图像是原始颜色。

我的代码有什么问题或需要更改?

提前致谢。

【问题讨论】:

    标签: android android-layout layout view viewgroup


    【解决方案1】:

    似乎有点可疑的是,您在 restoreView 中重置了两个矩阵,但没有重置其他两个。你不应该重置你的逆矩阵吗?由于您立即调用 invalidate,并且由于这将调用 invalidateChildInParent,然后映射点,我认为这可能是您的问题。

    这里的代码很酷,但是您确定覆盖 invalidateChildInParent 是安全的吗?有一条评论告诉你不要这样做。您确定没有其他方法可以转换子视图吗?抱歉,我以前没有做过这种类型的代码,所以我实际上并不知道正确的方法。听起来你几乎就在那里。祝你好运。

    【讨论】:

      猜你喜欢
      • 2020-11-14
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-06
      • 2021-06-21
      • 1970-01-01
      相关资源
      最近更新 更多