7

I am trying to do zoom using double finger touch gesture in the camera preview mode. But i unable to do that. I have done that Zoom control on the ImageView and it's working fine. Now i want to do that in the camera preview mode i.e. when we start the camera we can able to do zooming using double finger gesture.

Below i an adding the code for double finger gesture for zooming the image.

@Override
public boolean onTouch(View v, MotionEvent event) {
    // TODO Auto-generated method stub
    ImageView view = (ImageView) v;

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
        savedMatrix.set(matrix);
        start.set(event.getX(), event.getY());
        Log.d(TAG, "mode=DRAG");
        mode = DRAG;
        break;
    case MotionEvent.ACTION_POINTER_DOWN:
        oldDist = spacing(event);
        Log.d(TAG, "oldDist=" + oldDist);
        if (oldDist > 10f) {
            savedMatrix.set(matrix);
            midPoint(mid, event);
            mode = ZOOM;
            Log.d(TAG, "mode=ZOOM");
        }
        break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_POINTER_UP:
        mode = NONE;
        Log.d(TAG, "mode=NONE");
        break;
    case MotionEvent.ACTION_MOVE:
        if (mode == DRAG) {

            // for draging the image

              matrix.set(savedMatrix); matrix.postTranslate(event.getX() -
              start.x, event.getY() - start.y);

            break;
        } else if (mode == ZOOM) {
            float newDist = spacing(event);
            Log.d(TAG, "newDist=" + newDist);
            if (newDist > 10f) {
                matrix.set(savedMatrix);
                float scale = newDist / oldDist;
                matrix.postScale(scale, scale, mid.x, mid.y);
            }
        }
        break;
    }

    view.setImageMatrix(matrix);
    return true;
}

Please guide me how to do this gesture zoom control in camera preview. atleast provide a tutorial link.

With Thanks Vikash

3 Answers 3

10

Here is a solution I found on github: https://github.com/Betulaphobe/ChatCapsule/blob/8d4f00a7d4c9166aa7ce974670fdf3033a6064f4/chc_application/src/chc/helpers/CameraPreview.java

@Override
public boolean onTouchEvent(MotionEvent event) {
    // Get the pointer ID
    Camera.Parameters params = mCamera.getParameters();
    int action = event.getAction();


    if (event.getPointerCount() > 1) {
        // handle multi-touch events
        if (action == MotionEvent.ACTION_POINTER_DOWN) {
            mDist = getFingerSpacing(event);
        } else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
            mCamera.cancelAutoFocus();
            handleZoom(event, params);
        }
    } else {
        // handle single touch events
        if (action == MotionEvent.ACTION_UP) {
            handleFocus(event, params);
        }
    }
    return true;
}

private void handleZoom(MotionEvent event, Camera.Parameters params) {
    int maxZoom = params.getMaxZoom();
    int zoom = params.getZoom();
    float newDist = getFingerSpacing(event);
    if (newDist > mDist) {
        //zoom in
        if (zoom < maxZoom)
            zoom++;
    } else if (newDist < mDist) {
        //zoom out
        if (zoom > 0)
            zoom--;
    }
    mDist = newDist;
    params.setZoom(zoom);
    mCamera.setParameters(params);
}

public void handleFocus(MotionEvent event, Camera.Parameters params) {
    int pointerId = event.getPointerId(0);
    int pointerIndex = event.findPointerIndex(pointerId);
    // Get the pointer's current position
    float x = event.getX(pointerIndex);
    float y = event.getY(pointerIndex);

    List<String> supportedFocusModes = params.getSupportedFocusModes();
    if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean b, Camera camera) {
                // currently set to auto-focus on single touch
            }
        });
    }
}

/** Determine the space between the first two fingers */
private float getFingerSpacing(MotionEvent event) {
    // ...
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return FloatMath.sqrt(x * x + y * y);
}
3
  • This code snippet gave me issues in finding exact x,y positions.Could you help me with this?
    – user2875895
    Commented Nov 12, 2015 at 13:05
  • You can get the x,y coords from the MotionEvent. float x = event.getX(pointerIndex); float y = event.getY(pointerIndex);
    – mtbomb
    Commented Nov 12, 2015 at 17:13
  • What exactly is this x and y values doing in handleFocus ?? Seems like it is unused
    – Jas
    Commented Nov 16, 2015 at 4:39
3

I got the solution of my problem, Following is the solution.

 case MotionEvent.ACTION_MOVE:


              if (mode == ZOOM) {
                float newDist = spacing(event);

                double zoomDist = newDist-oldDist;

                if(zoomDist > 0){
                    if(zoomDist > 50 && zoomDist <= 200){

                        if (curZoomLevel < mZoomMax && gestureZoom == 0) {
                            gestureZoom ++;
                            GestureZoomIn();
                        }

                    }else if(zoomDist > 200 && zoomDist <= 300){


                        if (curZoomLevel < mZoomMax && gestureZoom == 1) {

                            gestureZoom ++;
                            GestureZoomIn();
                        }
                    }else if(zoomDist > 300 && zoomDist <= 400){

                        if (curZoomLevel < mZoomMax && gestureZoom == 2) {

                            gestureZoom++;
                            GestureZoomIn();
                        }

//

 private void GestureZoomIn(){
       if (mParameters.isZoomSupported()){
            mZoomMax = mParameters.getMaxZoom();
            if (zoom_text_value<mZoomMax) {
                zoom_text_value++;
                curZoomLevel++;
                zoom_float=zoom_float+0.5;
                onZoomValueChanged(curZoomLevel);

//

private void GestureZoomOut(){
       if (mParameters.isZoomSupported()){

            if (0<zoom_text_value) {
                zoom_text_value--;
                curZoomLevel--;
                zoom_float=zoom_float-0.5;
                onZoomValueChanged(curZoomLevel);

The above code i used to implement the multi zoom in camera review.

8
3

Take a look at my implementation. Support for both Camera APIs: camera and camera2. You can zoom by using double finger touch.

This is how I use the code on my Activities.

For camera API just:

private void setupZoomHandler(final Camera.Parameters parameters) {
    if ( parameters.isZoomSupported() ) {
        SimpleZoomHandlerBuilder.forView( mCameraPreview )
                .setMaxZoom( parameters.getMaxZoom() )
                .setZoomListener( new SimpleZoomHandler.IZoomHandlerListener() {
                    @Override
                    public void onZoomChanged(int newZoom) {
                        Camera.Parameters params = mCamera.getParameters();
                        params.setZoom( newZoom );
                        mCamera.setParameters( params );
                    }
                } )
                .build();
    }
}

And for camera2 API just:

private void setupZoomHandler(CameraCharacteristics cameraCharacteristics) {
    ActiveArrayZoomHandlerBuilder.forView( mTextureView )
            .setActiveArraySize( cameraCharacteristics.get( CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE ) )
            .setMaxZoom( cameraCharacteristics.get( CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM ) * 10 )
            .setZoomListener( new ActiveArrayZoomHandler.IZoomHandlerListener() {
                @Override
                public void onZoomChanged(Rect zoom) {
                    mCaptureRequestBuilder.set( CaptureRequest.SCALER_CROP_REGION, zoom );
                    updatePreview();
                }
            } )
            .build();
}

Here are the classes you need to zoom handling:

public abstract class AbstractZoomHandler implements View.OnTouchListener {

private static final int FINGER_SPACING_DELTA_FOR_ZOOM = 25;
private static final int FINGER_SPACING_ZOOM_INCREMENT = 5;

private static final float DEFAULT_ZOOM_HARDNESS = 0.4f;

private float lastFingerSpacingTime;
private float fingerSpacingBuffer;

protected int zoomLevel;
protected float zoomIncrement;
protected float maxZoom;

public AbstractZoomHandler(View touchableView) {
    touchableView.setOnTouchListener( this );
    this.lastFingerSpacingTime = 0;
    this.fingerSpacingBuffer = 0;
    this.zoomLevel = 1;
    setZoomHardness( DEFAULT_ZOOM_HARDNESS );
}

public void setMaxZoom(float maxZoom) {
    this.maxZoom = maxZoom;
}

public void setZoomHardness(float zoomHardness) {
    this.zoomIncrement = Math.round( zoomHardness * FINGER_SPACING_ZOOM_INCREMENT );
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    if ( !isPrepared() ) {
        return true;
    }
    if ( event.getAction() == MotionEvent.ACTION_UP ) {
        fingerSpacingBuffer = 0;
        return true;
    }
    if ( isTwoFingersTouchEvent( event ) ) {
        int newZoomLevel = performTwoFingersZoom( event );
        recalculateZoom( newZoomLevel );
    }
    return true;
}

private boolean isTwoFingersTouchEvent(MotionEvent event) {
    return event.getPointerCount() == 2;
}

private int performTwoFingersZoom(MotionEvent event) {
    int newZoomLevel = zoomLevel;
    float currentFingerSpacingTime = getFingerSpacing( event );
    fingerSpacingBuffer += currentFingerSpacingTime - lastFingerSpacingTime;
    if ( fingerSpacingBuffer >= FINGER_SPACING_DELTA_FOR_ZOOM && maxZoom > zoomLevel ) {
        newZoomLevel += zoomIncrement;
        fingerSpacingBuffer = 0;
    } else if ( fingerSpacingBuffer <= -FINGER_SPACING_DELTA_FOR_ZOOM && zoomLevel > 1 ) {
        newZoomLevel -= zoomIncrement;
        ;
        fingerSpacingBuffer = 0;
    }
    lastFingerSpacingTime = currentFingerSpacingTime;
    return newZoomLevel;
}

private void recalculateZoom(int newZoomLevel) {
    if ( newZoomLevel == zoomLevel ||
            newZoomLevel < 0 ||
            newZoomLevel > maxZoom) {
        return;
    }
    zoomLevel = newZoomLevel;
    notifyZoomChanged( zoomLevel );
}

private float getFingerSpacing(MotionEvent event) {
    float x = event.getX( 0 ) - event.getX( 1 );
    float y = event.getY( 0 ) - event.getY( 1 );
    return (float) Math.sqrt( x * x + y * y );
}

public abstract void notifyZoomChanged(int zoom);

public abstract boolean isPrepared();

}

For Camera:

public class SimpleZoomHandler extends AbstractZoomHandler {

private IZoomHandlerListener zoomHandlerListener;

SimpleZoomHandler(View touchableView) {
    super( touchableView );
}

public void setZoomHandlerListener(IZoomHandlerListener zoomHandlerListener) {
    this.zoomHandlerListener = zoomHandlerListener;
}

@Override
public void notifyZoomChanged(int zoom) {
    zoomHandlerListener.onZoomChanged( zoom );
}

@Override
public boolean isPrepared() {
    return zoomHandlerListener != null;
}

public interface IZoomHandlerListener {

    void onZoomChanged(int newZoom);

}
}

And for Camera2:

public class ActiveArrayZoomHandler extends AbstractZoomHandler {

private IZoomHandlerListener zoomHandlerListener;
private Rect activeArraySize;

ActiveArrayZoomHandler(View touchableView) {
    super( touchableView );
}

public void setZoomHandlerListener(IZoomHandlerListener zoomHandlerListener) {
    this.zoomHandlerListener = zoomHandlerListener;
}

public void setActiveArraySize(Rect activeArraySize) {
    this.activeArraySize = activeArraySize;
}


@Override
public void notifyZoomChanged(int zoom) {
    int minW = (int) (activeArraySize.width() / maxZoom);
    int minH = (int) (activeArraySize.height() / maxZoom);
    int difW = activeArraySize.width() - minW;
    int difH = activeArraySize.height() - minH;
    int cropW = difW / 100 * zoomLevel;
    int cropH = difH / 100 * zoomLevel;
    cropW -= cropW & 3;
    cropH -= cropH & 3;
    Rect zoomRect = new Rect( cropW, cropH, activeArraySize.width() - cropW, activeArraySize.height() - cropH );
    zoomHandlerListener.onZoomChanged( zoomRect );
}

@Override
public boolean isPrepared() {
    return zoomHandlerListener != null && activeArraySize != null;
}

public interface IZoomHandlerListener {

    void onZoomChanged(Rect zoom);

}
}

And their respective Builder to make it fluent interface. For camera API:

public class SimpleZoomHandlerBuilder {

private SimpleZoomHandler simpleZoomHandler;

public static SimpleZoomHandlerBuilder forView(View touchableView) {
    return new SimpleZoomHandlerBuilder( touchableView );
}

private SimpleZoomHandlerBuilder(View touchableView) {
    simpleZoomHandler = new SimpleZoomHandler( touchableView );
}

public SimpleZoomHandlerBuilder setZoomListener(SimpleZoomHandler.IZoomHandlerListener listener) {
    simpleZoomHandler.setZoomHandlerListener( listener );
    return this;
}

public SimpleZoomHandlerBuilder setMaxZoom(float maxZoom) {
    simpleZoomHandler.setMaxZoom( maxZoom );
    return this;
}

public SimpleZoomHandler build() {
    return simpleZoomHandler;
}

}

And for camera2 API:

public class ActiveArrayZoomHandlerBuilder {

private ActiveArrayZoomHandler activeArrayZoomHandler;

public static ActiveArrayZoomHandlerBuilder forView(View touchableView) {
    return new ActiveArrayZoomHandlerBuilder( touchableView );
}

private ActiveArrayZoomHandlerBuilder(View touchableView) {
    activeArrayZoomHandler = new ActiveArrayZoomHandler( touchableView );
}

public ActiveArrayZoomHandlerBuilder setZoomListener(ActiveArrayZoomHandler.IZoomHandlerListener listener) {
    activeArrayZoomHandler.setZoomHandlerListener( listener );
    return this;
}

public ActiveArrayZoomHandlerBuilder setMaxZoom(float maxZoom) {
    activeArrayZoomHandler.setMaxZoom( maxZoom );
    return this;
}

public ActiveArrayZoomHandlerBuilder setActiveArraySize(Rect activeArraySize) {
    activeArrayZoomHandler.setActiveArraySize( activeArraySize );
    return this;
}

public ActiveArrayZoomHandler build() {
    return activeArrayZoomHandler;
}

}
4
  • 1
    It works fine. Can we implement onTapFocus along with zoom? Commented May 6, 2019 at 5:25
  • @PeddiRaju how is the implementation for updatePreview(); in camera2 ? Commented May 15, 2019 at 1:32
  • I have discovered = captureSession.setRepeatingRequest(previewRequestBuilder.build(), captureCallback, backgroundHandler); Commented May 15, 2019 at 1:52
  • @AndroidRuntimeException getFingerSpacing getting pointerindex exception how to resolve it in x and y coordinates
    – newxamarin
    Commented Aug 11, 2021 at 12:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.