Making Zoom View in PSLab Android app

This blog demonstrates how to make a zoom view in an Android app by taking example from one made in PSLab Android app. It will mainly reflect the work done under PR #1117 in PSLab Android repository. The demonstration shown in this blog is for zooming a complete layout. But individual components of a layout can also be given this zoom effect.

How to make a zoom view?

Below is a step by step guide on how to implement a zoom view in an Android app :

  • First make a  Zoom Layout class in Android Project which will further include GestureDetector, MotionEvent, etc.
  • Now extend the Zoom Layout class from a base layout provided by Android i.e. Relative Layout, Linear Layout, etc. as per need because we need to give zoom effect to a complete layout. In this demonstration, I will use the Relative Layout class as my base class.
  • Also to detect the gestures made by a user, we need to implement the ScaleGestureDetector.OnScaleGestureListener class. So, finally, the class implementation will look like this
public class ZoomLayout extends RelativeLayout implements ScaleGestureDetector.OnScaleGestureListener {
}
  • Now make default constructors and declare variables to define the range of the minimum and maximum possible zoom, coordinates before drag, coordinates after drag, etc.
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
private float startX = 0f;
private float startY = 0f;
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;

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

Here startX and startY are the initial coordinates of the layout, dx and dy are the new coordinates of the layout and prevDx and prevDy are the coordinates of the previous location of the layout. Also, mode is the current mode of the gesture which will be further elaborated upon in coming steps, and all other remaining variables are for scaling the screen on gesture movements. Also, init(context) is a method which will be explained in step 5.

  • Now, we will make a method named init() to initiate the process of scaling the layout on gesture detection.
public void init(Context context) {
        final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
        this.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
                    case MotionEvent.ACTION_DOWN:
                        if (scale > MIN_ZOOM) {
                            mode = Mode.DRAG;
                            startX = motionEvent.getX() - prevDx;
                            startY = motionEvent.getY() - prevDy;
                        }
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if (mode == Mode.DRAG) {
                            dx = motionEvent.getX() - startX;
                            dy = motionEvent.getY() - startY;
                        }
                        break;
                    case MotionEvent.ACTION_POINTER_DOWN:
                        mode = Mode.ZOOM;
                        break;
                    case MotionEvent.ACTION_POINTER_UP:
                        mode = Mode.DRAG;
                        break;
                    case MotionEvent.ACTION_UP:
                        mode = Mode.NONE;
                        prevDx = dx;
                        prevDy = dy;
                        break;
                    default:
                        mode = Mode.NONE;
                        prevDx = dx;
                        prevDy = dy;
                        break;
                }
                scaleDetector.onTouchEvent(motionEvent);

                if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
                    float maxDy = (child().getHeight() - (child().getHeight() / scale)) * scale;
                    dx = Math.min(Math.max(dx, -maxDx), maxDx);
                    dy = Math.min(Math.max(dy, -maxDy), maxDy);
                    applyScaleAndTranslation();
                }
                return true;
            }
        });
    }

The detailed explanation of the above code snippet is as follows:

  1. scaleDetector – A gesture detector variable to store the scaling of the screen i.e. how much the screen is zoomed
  2. onTouch() – It is the main method handling the calculations for zooming the layout and setting the position of the zoomed layout. The view attribute is the current view of the layout and the motionEvent attribute handles the different task for different gestures made by a user.

Here the mode variable is used to define one of the three gestures i.e. NONE, DRAG or ZOOM where

  1. NONE – No gesture detected on the screen
  2. DRAG – Sliding gestures are made
  3. ZOOM – Pinch gesture is made

Also, a detailed explanation of the motion events used in the switch case can be found out in the resources [1].

After the switch case, the if statement is used to do calculations based on the current child in focus and the previous coordinates if and only if the zoom hasn’t reached the maximum limit and the view is dragged to see the zoomed contents. Method getParent().requestDisallowInterceptTouchEvent(true) is used to disable the scroll effect of the parent layout if any. In this case, the zoomed layout is inside a bottom sheet and so by using this method, the bottom sheet isn’t closed on swipe down gesture.

  • Now create applyScaleAndTransition() method and child() method used in step 5.
private View child() {
        return getChildAt(0);
    }

This method is used to return the current child layout in focus i.e. visible on the screen.

private void applyScaleAndTranslation() {
        child().setScaleX(scale);
        child().setScaleY(scale);
        child().setTranslationX(dx);
        child().setTranslationY(dy);
    }

This method is used to apply the final calculations that are done in step 5 to the child layout in focus.

So, now the Zoom Layout is ready for use and can be used as same as we use the Relative Layout in the XML files. The final output produced by using the Zoom Layout as a child of bottom sheet in PSLab Android app is as shown in figure 1.

Figure 1. Demonstration of Zoom Layout made in PSLab Android app

Resources

  1. https://developer.android.com/reference/android/view/MotionEvent – Documentation of motion event gestures in android

Zooming Feature in the Phimpme Android’s Camera

The Phimpme Android application comes with a complete package of camera, Edit images, sharing and gallery functionalities. It has a well featured and fully functional camera with all the capabilities that a user expects from a camera application. One such feature in the Phimpme Android application is the zooming functionality. It provides the user the option to zoom in using the pinch gesture of the fingers or the user can select the settings to zoom in from the volume buttons. In this tutorial, I will be explaining how I achieved the zooming functionality in the Phimpme Android app.

Step 1

The first thing we need to do is to check whether the device will support the zoom in functionality or not to avoid random crashes while runtime of the application and while performing the zoom action in case the camera of the device doesn’t support this feature. This can be done by the following lines of code:

Camera.Parameters params = mCamera.getParameters();
Boolean supports = params.isZoomSupported();

Step 2

Now after getting the camera parameters and checking whether the camera supports the zoom in functionality, we need to add the touch listener to the surface view of the camera so that we can get the touch locations and the finger spacing of the user to get the pinch to zoom in functionality. This can be done using the following line of code.

surfaceView.setOnTouchListener(this);

Whenever the user touches the screen this touch listener gives a callback to the overridden onTouchEvent method and passes the MotionEvent to the function. The motion event object in Android handles the movement reports. Now in the onTouchEvent method, we calculate the finger spacing between the two fingers and calculate the approximate amount by which the user wants to zoom in. The finger spacing can be calculated using the following lines of code.

float x = event.getX(0) - event.getX(1);
   float y = event.getY(0) - event.getY(1);
   return FloatMath.sqrt(x * x + y * y);

After getting the finger spacing we need to cancel the auto focus of the camera before performing the zoom action so that the application does not crash. This can be achieved by a single line of code below.

mCamera.cancelAutoFocus();

Step 3

The final step is to set the zoom level in the camera application by calculating the zoom level by using the finger spacing. For this, first we need to get the max zoom level supported by the device so that we do not apply the zoom level that is not supported by the device. The calculation of max zoom level and setting of the desired zoom level by the user can be performed by using the following lines of code.

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

This is how we have achieved the functionality of zooming in and clicking pictures in the Phimpme Android application. To get the full source code and to know how to use the volume control buttons to zoom in/out, please refer to the Phimpme Android repository.

Resources

  1. GitHub – Open camera source code : https://github.com/almalence/OpenCamera
  2. Android developer’s guide – MotionEvents in Android : https://developer.android.com/reference/android/view/MotionEvent.html
  3. StackOverflow – Pinch to zoom functionality : https://stackoverflow.com/questions/8120753/android-camera-preview-zoom-using-double-finger-touch
  4. GitHub – Phimpme Android repository : https://github.com/fossasia/phimpme-android