Question

Hi I'm trying to implement pinch to zoom functionality inside a LinearLayout, my first attempts have been to extend linearlayout and use a scale gesture detector to detect the pinch to zoom gesture, following the suggestions from the android developers blog

I tried to override the onDraw method and set the scaling using the canvas, but it is not working at all, the view remains fixed size

This is the code inside my custom view:

private static final int INVALID_POINTER_ID = -1;

private static final String TAG = "MyTag";

private float mScaleFactor = 1f;
private ScaleGestureDetector mScaleDetector;
private float mPosX;
private float mPosY;
private float mFocusX;
private float mFocusY;

private float mLastTouchX;
private float mLastTouchY;
private int mActivePointerId = INVALID_POINTER_ID;

private boolean mHandlingGesture = true;

public CrosswordGridLayout(Context context) {
    super(context);
    init();
}

public CrosswordGridLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public CrosswordGridLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
}

private void init() {
    setWillNotDraw(false);
    mScaleDetector = new ScaleGestureDetector(getContext(),
            new ScaleListener());
}

@Override
protected void onDraw(Canvas canvas) {
    Log.d(TAG, "onDraw");
    canvas.save();
    canvas.translate(mPosX, mPosY);
    canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
    super.onDraw(canvas);
    canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
    Log.d(TAG, "onTouchEvent");
    // Let the ScaleGestureDetector inspect all events.
    mHandlingGesture = 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;
        mActivePointerId = ev.getPointerId(0);
        break;
    }

    case MotionEvent.ACTION_MOVE: {
        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
        final float x = ev.getX(pointerIndex);
        final float y = ev.getY(pointerIndex);

        // Only move if the ScaleGestureDetector isn't processing a gesture.
        if (!mScaleDetector.isInProgress()) {
            final float dx = x - mLastTouchX;
            final float dy = y - mLastTouchY;

            mPosX += dx;
            mPosY += dy;

            requestLayout();
        }

        mLastTouchX = x;
        mLastTouchY = y;

        break;
    }

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

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

    case MotionEvent.ACTION_POINTER_UP: {
        final int pointerIndex = (ev.getAction() & 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;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return true;
}

private class ScaleListener extends
        ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        Log.d(TAG, "onScale");
        mScaleFactor *= detector.getScaleFactor();

        // Don't let the object get too small or too large.
        mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));

        mFocusX = detector.getFocusX();
        mFocusY = detector.getFocusY();
        Log.d(TAG, "mScaleFactor=" + mScaleFactor);
        requestLayout();

        return true;
    }
}
Was it helpful?

Solution

Do you canvas operations inside dispatchDraw() not onDraw()

protected void dispatchDraw(Canvas canvas) {
    Log.d(TAG, "dispatchDraw");
    canvas.save();
    canvas.translate(mPosX, mPosY);
    canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
    super.dispatchDraw(canvas);
    canvas.restore();
}

The difference between that example and you code is that in the example it is simple View that is scaled and in you case it's ViewGroup.

And don't use requestLayout() in ScaleListener.onScale() use invalidate() instead.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top