Welcome to the third part of the Android tutorial on how to make your own zoom control like the one used in Sony Ericsson X10 Mini in the Camera and Album applications. Click here to read the second part of the tutorial.

Don’t forget to go to Android Market and download Sony Ericsson Tutorials, the app that collects all sample apps in this and other Sony Ericsson tutorials. Get the QR-code for the app here. Below is a link to the source code of part 3, prepared for you to set up your own project in e.g. Eclipse.

[Download] One Finger Zoom sample project – Part 3 (215kb)

This tutorial part will focus on introducing a new way of interacting with the zoom, a new input paradigm as our designers would say. In the previous tutorial we laid the ground for exactly this when we created a new class for controlling the zoom state. Separating the state control from the input control handled by an OnTouchListener implementation.

Besides being nice for easier code overview this separation is great for us in at least two other ways:

First it it is very useful for trying out different way of controlling the zoom, something I had great use for in the X10 mini project. Here at Sony Ericsson we have the privilege of working with several great and driven designers wanting to test out every idea for controlling the zoom you can think of. Being able to easily switch between variants meant we could evaluate several good designs before settling.

Secondly it means we can easily have different ways of controlling the zoom on different devices. Multiple screen support is something you should always think of when designing for android. For small devices like the X10 mini one handed touch is very useful, while larger screens will be operated with two hands most of the time, one hand holding the phone and another free to touch the screen.

Implementing a new paradigm

As you might remember, the zoom control in the previous tutorial parts had options menu alternatives for switching between pan and zoom mode. Having menu alternatives such as these are usually not a good idea as in many Android applications the options menu is already overflowing. Also you don’t want navigation more than one click away.

Our new input paradigm will be similar to the one on the X10 mini. We’ll use long press to enter zoom mode and vertical touch dragging for zooming in and out. Pan will be left unchanged, activated simply by dragging.

We’ll start the implementation of a new listener by modifying the BasicZoomListener class we’ve built on so far, removing all references to the ControlType enum and renaming the class to LongPressZoomListener. Then we create a new constructor for the listener.

[java]
private final int mScaledTouchSlop;
private final int mLongPressTimeout;

public LongPressZoomListener(Context context) {
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
[/java]

To implement long press we need to define the long press timeout, that is the duration before a press turns into a long press. We also need to define the touch slop, that is the amount touch can wander before we decide it’s a drag event rather than a long press. As luck might have it the system defines good default values we can use for this. The touch slop value is even screen size dependent.

To implement the long touch logic we first need to define a flag for holding the classification of the current touch event. The classification mode have three states, undefined means the touch hasn’t been classified yet and can become either pan or zoom. Pan means the user has started dragging and has entered pan mode while zoom means the user has made a long press and entered zoom mode where vertical touch movement zooms in or out.

[java]
private enum Mode {
UNDEFINED, PAN, ZOOM
}

private Mode mMode = Mode.UNDEFINED;
[/java]

Next up we will re-implement the onTouch callback method to classify long press and drag and interpret these as either zoom or pan.

[java]
private final Runnable mLongPressRunnable = new Runnable() {
public void run() {
mMode = Mode.ZOOM;
}
};

public boolean onTouch(View v, MotionEvent event) {
final int action = event.getAction();
final float x = event.getX();
final float y = event.getY();

switch (action) {
case MotionEvent.ACTION_DOWN:
v.postDelayed(mLongPressRunnable, mLongPressTimeout);
mDownX = x;
mDownY = y;
mX = x;
mY = y;
break;
[/java]

Starting with the action down event, this is largely unchanged from our previous tutorials with the exception of posting a runnable on the view handler. This runnable, called mLongPressRunnable, is posted with a delay meaning it will run once the delay time has passed, in this case when the long press duration has passed. The runnable simply sets the mode to zoom mode, meaning once the long press duration has passed we’ll enter zoom mode.

[java]
case MotionEvent.ACTION_MOVE: {
final float dx = (x – mX) / v.getWidth();
final float dy = (y – mY) / v.getHeight();

if (mMode == Mode.ZOOM) {
mZoomControl.zoom(
(float)Math.pow(20, -dy),
mDownX / v.getWidth(),
mDownY / v.getHeight());
} else if (mMode == Mode.PAN) {
mZoomControl.pan(-dx, -dy);
} else {
final float scrollX = mDownX – x;
final float scrollY = mDownY – y;

final float dist = (float)
Math.sqrt(scrollX * scrollX + scrollY * scrollY);

if (dist >= mScaledTouchSlop) {
v.removeCallbacks(mLongPressRunnable);
mMode = Mode.PAN;
}
}

mX = x;
mY = y;
break;
}
[/java]

The action move event handling code is also similar to what we had in the previous tutorials. What’s new is the else block where we check if the the touch has wandered far enough to be classified as a drag. This is done by calculating the distance from the touch down location using the Pythagorean theorem and comparing it to the touch slop we got for the view configuration. If the touch has wandered far enough we set the pan mode flag and we remove the mLongPressRunnable from the view callback queue, making sure we don’t enter zoom mode after the long press timeout duration has passed.

[java]
default:
v.removeCallbacks(mLongPressRunnable);
mMode = Mode.UNDEFINED;
break;

}

return true;
}
[/java]

Finally we add support for handling the remaining touch events, such as action up and action cancel. In these cases we remove the long press callback and reset the mode flag, putting the component back into default state.

Tactile feedback

Now we have implemented a nicer input paradigm, making it easy and swift to zoom and pan. It is however it’s a bit confusing to the user that no indication is given when they enter zoom or pan mode. To fix this we’ll use tactile feedback, giving the user a quick vibration when entering zoom mode.

Using the vibrator on Android devices is easy. We get access to the vibrator service through the context we get in the constructor like this.

[java]
private final Vibrator mVibrator;

public LongPressZoomListener(Context context) {
mLongPressTimeout = ViewConfiguration.getLongPressTimeout();
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mVibrator = (Vibrator)context.getSystemService(“vibrator”);
}
[/java]

Then we modify our runnable to give a quick vibration when entering zoom mode, 50 ms gives a nice and short feedback.

[java]
private static final long VIBRATE_TIME = 50;

private final Runnable mLongPressRunnable = new Runnable() {
public void run() {
mMode = Mode.ZOOM;
mVibrator.vibrate(VIBRATE_TIME);
}
};
[/java]

This is all the java code required for tactile feedback, however if you try to run this you’ll get an exception due to the application not having permission to use the vibrator service. This is easily remedied by adding the line below in the manifest.

[xml]
<uses-permission android:name=”android.permission.VIBRATE” />
[/xml]

And we’re done! Our zoom is starting to feel quite nice and useful, we’ve got a nice input paradigm and a zoom that behaves reasonably. This is the perfect time for you readers to try out your own take on a input paradigm if you want to, or maybe you’ve got ideas on how to improve the one we’ve just implemented.

Next up we’ll implement a very nice addition to our zoom, fling and bounce. Currently a downside with the zoom is that it feels stiff and panning far requires a lot of dragging. This will all change in the next tutorial where we introduce a more dynamic behavior.

Until then, good luck!

Sort by

  • By Manish
    9th June 2010.
    21:57

    Thank you for the Nice tutorial, i guess i’ll add some ideas to it.( i have a few ideas in mind! ) But for now, i need to study the whole thing very carefully. :)

    Thumb up 0 Thumb down 0

  • By tacone
    10th June 2010.
    00:29

    Looking forward to the last part of the tutorials (zoom and 3d list) !

    Thumb up 0 Thumb down 0

  • By Andreas Agvard
    10th June 2010.
    11:23

    @Manish, looking forward to hear your ideas!

    Thumb up 0 Thumb down 0

  • By Andreas Agvard
    10th June 2010.
    11:24

    @tacone, third part of 3D list is scheduled for next week

    Thumb up 0 Thumb down 1

  • By tacone
    10th June 2010.
    13:14

    Thanks Andreas,
    sometimes I wish the publishing rhythm could be faster :) .

    Keep up the good work.

    Thumb up 0 Thumb down 0

  • By Roel
    10th June 2010.
    23:22

    Thanks for these tutorials. They’re very insightful.

    Thumb up 0 Thumb down 0

  • By Florian
    7th July 2010.
    12:42

    Hey, Im looking forward for tghe last part of the zoom tutorial, especially for the source code: Dynamic behavior :)

    Thumb up 0 Thumb down 0

  • By Andreas Agvard
    7th July 2010.
    14:32

    @Florian
    You need not wait much longer, I promise! :)

    Thumb up 0 Thumb down 0

  • By abhishek
    14th September 2010.
    10:34

    hi… i bought new sony ericson’s mini pro X10 on contract and surprisingly there is no zoom when you want to take a pic…i mean you can zoom in but once you clicked a pic…which is ridiculous….it has 5 mega pixel…i am so disappointed with you people…did not expect this from Sony…which is such a brand name in the market… i have one question can we integrate a camera option in it which has zoom in option when i want to take a pic….thnx

    Thumb up 0 Thumb down 1

  • By ammar
    3rd February 2011.
    03:38

    hi
    thanks for the great tutorial
    i just wonder in zoom state
    can we lock the position of the image rather than having the panning?

    thanks

    Thumb up 0 Thumb down 1