简体   繁体   中英

Android: ImageView click is not get perfect after reset zoom

I have multiple dynamic images which is displayed on basis of X,Y,Height,Width. These images are bind with RelativeLayout and this layout is bind with my ZoomableViewGroup class. When I click on the image,the color is fill in it.

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();
}
}

Zoom in/Out is working perfectly.The restoreView() fuction is used show View in original state and it works fine. But after restore in original state,the ImageView's click in View is not get perfectly click. It means color is not fill in whole image like half color is fill and half image is with original color.

What's wrong or changes to be require in my code?

Thanks in advance.

One thing that seems a bit suspicious is that you reset two of your matrices in restoreView, but not the other two. Shouldn't you reset your inverse matrices too? Since you call invalidate right away, and since that will call invalidateChildInParent, which then maps the points, I think that might be your problem.

It's cool code that you have here, but are you sure it's safe to override invalidateChildInParent? There is a comment that tells you not to do so. Are you sure there's not another way to transform child views? Sorry, I haven't done this type of code before, so I don't actually know the right way to do that. It sounds like you're almost there anyway. Good luck.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM