Multitouch Panning and Zooming Examples for Android

I have done some work to help me understand multitouch features in Android. I built a simple demo with examples of pinch zooming and panning. Most of the examples involve using drawing operations on the canvas of a view.


About two years ago, I started some work on moving images in Android. I started with the “Making Sense of Multitouch” blog article and ended up doing some simple things with moving images and then drag-drop based on the Android Launcher (see references for links). It’s interesting to me that I have gone back to that same article for a different purpose. I am working on apps where the basic zoom and pan operations are needed.

Overview of the Examples

1 Basic Multitouch

The first example does the basics of multitouch as explained in the Multitouch article. An image displays on the screen. You can touch the image and move it around. You can also use two fingers to do pinch-zoom and zoom out.


I did this as the first step because I wanted to be sure that I understood the Multitouch article and had a working example.

To understand how this example works, go through the Mulitouch article and then get the source code for my examples (see below). My adaptation of the Multitouch article is in class PanZoomView.

 * This view supports both zooming and panning.
 * What gets shown in the view depends on the onDraw method provided by subclasses.
 * The default onDraw method defined displays R.drawable.zoom_view_sample.
public class PanZoomView extends View

The key things to understand in the article and the code are these:

  • How the canvas moves around. See the call to “canvas.translate(x, y)” in method onDraw.
  • How the canvas is scaled during a pinch-zoom gesture. See the call to :canvas.scale(mScaleFactor, mScaleFactor)” in method onDraw.
  • How onTouchEvent handles touch and multitouch events. It sets variables so the calls to translate and scale work.

2 Canvas

In the second section, there are two examples that do essentially the same thing for handling panning and zooming as the first one. The difference is that instead of an image being moved around and zoomed, it is a circle and a rectangle. Those are drawn on the canvas in the onDraw method of the view.

zoom-pan-05 zoom-pan-06

I did these as the next step in understanding how to build my views for my own app. Zooming an image was step one. Zooming a custom canvas was step 2. To help me understand pinch zoom and the point around which zooming occurs, I display a small circle at the point of focus for the scaling of the view. Place two fingers on the screen and look for that dot, halfway between your two fingers.

The Circles example supports both panning and zooming. The Rectangles example supports only zooming. I found myself getting a bit confused while trying to understand for translation on the canvas and scaling so both examples were useful to me.

3. Pan Zoom Listener

PanZoomListener is an interesting example. It comes from Java Pan / Zoom listener for Android (reference 2 below). Rather than doing a custom view every time you want to have panning and zooming, which is what all the other examples do, you simply set up a listener on a view you already have working.

FrameLayout f = new FrameLayout(context);
FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams (...);
imageView imageView = new ImageView (this);
view.addView (imageView, fp);
view.setOnTouchListener(new PanAndZoomListener(view, imageView, Anchor.TOPLEFT));

That may look like a lot to do, but it’s not nearly as complicated as what you do when you build a view of your own with custom handling of panning and zooming.

It works pretty well, but there are a few things that you’d need to adjust because not all of the operations are completely smooth. I definitely like the idea. I adapted the code and built my own example.

What you see in the example I did is an image. As soon as you touch it, it resizes and centers itself. After that, you can use pinch zoom on it. The focus of the zoom is the point between your two fingers.

zoom-pan-09 zoom-pan-10

4. Image Squares

This example was another step toward getting the right panning and zooming effects in the app that I am working on. In the app, I want to be able to draw a grid of squares on a canvas. In the example, I don’t draw the entire grid but simply some squares along a diagonal from top to bottom. This example supports zooming but not moving the canvas around as you touch the screen with one finger. The focus point of the zoom is shown with a small circle again.


You cannot move the canvas around with a simple touch. However, if you start a pinch-zoom gesture and then move both fingers around the image does shift. I never figured out exactly what was causing that. So watch out for that if build on this example.

5. Fixed Point Zoom

Since I was having a few problems with the canvas jumping around a bit, I decided to try using a fixed point for zooming. The last  example zooms in and out around the center dot shown on the screen.

zoom-pan-11 zoom-pan-14

The code structure of this example is a bit different than the earlier ones. All of the views in these examples are subclasses of a PanZoomView class. The view used in this example is LargeGridView. It defines its own drawOnCanvas method like all the rest do, but it also defines its own onDraw method. By the time I got here, I found that the flexible framework I built in PanZoomView to explore the different aspects of panning and zooming wasn’t quite right for fixed point zooming.

Fixed point zoom is what I have ended up using in a couple of apps.

(UPDATE – Feb 16, 2016. I have done even more with fixed point zooming. The latest article on this topic is for a zoomable game board. See Android Example of a Zoomable Game Board.)


I have done this article a bit differently than some of my earlier blog tutorials. I have not included as much code and explanations.  I assume that most readers will dig into the code as they need to. Download the source code using the links in the next section.

What I wanted to do here is show the progression of my thinking and understanding as I worked on an interesting problem. If you are new to Android, I suggest following a similar progression. Start simple and add more complex features as your understanding increases.

Source Code

You can download the source code for this demo from  the website. Click here: download zip file from The zip is attached at the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 4.2 (API 17). It works in all API levels from API 10 on up.

Related Articles

Making Sense of Multitouch – I recommend this to anyone looking for a good starting point for touch handling in Android.

Java / Pan Zoom Listener for Android – Article that describes the PanZoomListener. Definitely, take a look at this one.

Moving Views in Android – Part 1 – a simple example that moves an image when you click a button

Moving Views in Android – Part 2, Drag and Drop – an example based on the Launcher code of Android 2.2.

Android Example of a Zoomable Game Board – my latest article on the topic of panning and zooming. new

About Bill Lahti

Bill Lahti is a software engineer building mobile applications and knowledge management solutions. Two of his interests are writing for this blog and building Android apps, with strategy games being an area of particular interest.
This entry was posted in Android and tagged , , , , . Bookmark the permalink.

35 Responses to Multitouch Panning and Zooming Examples for Android

  1. Michael A. says:

    Neat. I just wish that there was a good way to handle large bitmaps in Android on the 2D view, without instantly running out of memory. Pinching to zoom isn’t super-relevant if the image is too low resolution to be usable.

    It surprises me a bit that there don’t exist any really low complexity OpenGL frameworks/examples for Android (i.e., Canvas with OpenGL).

    • blahti says:

      I am working on two apps where I have to be careful with memory. In one app, I create a canvas that is three times the shortest screen width and I am to zoom in and out on that. For example, on a Nexus 7, it is roughly 2400 x 2400. Since there is only one of those bitmaps active at a time, the app does not have memory problems. In another app, users can submit images and pictures from their phone or tablet to a server and then browse the set of all images from all users on their device. I maintain a cache of images in memory and on the SD card and size internal bitmaps so they are reasonable for the screen of each device. I learned all of this from a great article and working example on the Android Developers website: Display Bitmaps Efficiently. You might want to read that and see if it gives you any ideas on how to manage images in your own app.

      • Michael A. says:

        The Nexus 7 has a generous 48Mb heap, doesn’t it? So not really a problem with a 24Mb image for that device – you’d have trouble running it on a 16Mb mdpi device (13Mb size). Given the number of OOMs I still see regularly, I’m pretty sure there are still some pretty low memory devices out in the wild (in my most popular app, I use a 1024×1024 base image that I overlay smaller sprites on). OOMs are – by a huge margin – the most commonly reported error.😦

        To be fair, it has a lot of users and most people apparently don’t have any problems, but it is still extremely annoying to see those 10+ reports every week.

        I use a singleton “ImageManager” class, which loads the images using SoftReferences, pretty much as accepted wisdom dictates. The only image I cannot soft-load is the main image, as the accidental purging that would kill graphical performance. Apparently, that locks up enough memory to cause the OOMs on some devices. And I’m not even convinced soft-referencing the large image would help either – Android is just slow at recovering memory from images.

        One approach I’ve tried is to tile large images, and that seemingly works – albeit with some occasional stuttering from time to time if scrolling goes a bit too fast. It wouldn’t work for zooming, though, as one would still have to get the entire image into memory if the thing is completely zoomed out.

      • blahti says:

        In “Loading Large Bitmaps Efficiently“, they talk about loading a scaled down version of a bitmap into memory by setting inSampleSize. Is that what you do when running on a 16Mb mdpi device, which I assume is much lower resolution than a Nexus 7? Wouldn’t that way of loading let you choose a reasonable size that would still fit in memory on the smaller device? Or is the loss in quality of the image unacceptable?

      • Michael A. says:

        I never used inSampleSize on 16mb devices for 1024×1024 images; in theory, they shouldn’t cause problems (practice is another matter of course).

        Low resolution images are not very useful games. The problem with all of
        Google’s comments on handling of large images is that their default answer is always: “well, you don’t _really_ want to use that large image, do you?” But yes, yes I do.

        I’ve never considered using inSamplesize for zooming. Loading in new scaled down images in the middle of updating the screen sounds like the sort of thing that would lead to even more stuttering as the old image set gets purged – and the tiled approach is already noticeable enough as is that I’m not sure I’d want to use it “in the wild”.

        Haven’t tried to do it, however.. Might be worth a try, though at the moment I’m leaning more toward looking at something like this very simple OpenGL skeleton code (, and seeing if I can make time to extend it into a very simple 2D rendering framework for large image scrolling/zooming – assuming one can move most of the textures out of the heap.

        Meh – I’m just grumbling because I work on a corner use case that very few others seem to care for.🙂

      • blahti says:

        The time I used this it worked out well. I started with the high resolution image, but when I was ready to display it, I used inSampleSize to reduce the size of the bitmap I created. I did some experimentation so I knew what sampling worked for various device screen sizes and densities. I tried not to sacrifice quality to the point where the images looked bad.

      • Michael A. says:

        Well, I guess it is something I will have to look into. Actually, simply discussing this got me annoyed/interested enough to do some research on the problem again, and I fell across this OS project which seems to do exactly what I’d like to be able to do:

        Will need to take a look at that, and see if I can make something of it that works. It bugs me enough that I’d seriously consider taking the effort to clean up my code and open-source it if I managed a good “generic” solution.

      • Michael A. says:

        The WorldMap example that I linked above gave me the final pieces of the ideas required to solve this problem to my satisfaction, and – having made a basic implementation that I’m satisfied with, I’ve now released it as open-source.

        The idea is basically Lombardo’s, but I built it using a SurfaceView/Renderer structure and – like you discuss above – chose to zoom around a center point (Lombardo for some reason chose to zoom around the left upper corner, which really doesn’t work as a generic solution).

        Still needs a bit of work. The trick is really in the idea of using a downsampled version to cover up while retrieving the “real version”, but it doesn’t look as good as it could when there is a big gap in size between the detailed and downsampled version. One thing that could be useful would be let the background dynamically downsample what it fetches depending on the ScaleFactor. This would make the transition from highres to lowres less jarring when working with those really huge images. I should probably also do a few examples to demonstrate how this can be used.

        Thanks for the discussion and all the stuff you put on your blog.

      • blahti says:

        Thank you for sharing your thoughts and that code. I will try it out.

  2. Pingback: Layer drawable zooms in automatically : Android Community - For Application Development

  3. Sara says:

    Thanks for this helpful Demo .. but I have an issue, my image view doesn’t appear until I touch it and I don’t know why??

    • blahti says:

      Does this happen in the original source code or after you have made modifications or adapted it to your own app?

      • Slayer9108 says:

        Thanks for the code! its help me alot!! But I have that same issue that Sara :/ in the original source, the image moves to the center when i touch it, in my app, the imageView doesn’t show anything until i touch it.. (3. Pan Zoom Listener)

      • Bill Lahti says:

        These examples were things I tried, and sadly, I did not necessarily perfect all of them. I remember that one being particularly frustrating and never figured it out. I moved on to scaling around a fixed center point and used that in a game I am working on.

        Since writing about this, I have learned a bit more about translation and scaling of images. If I were to go back and try to figure it out, I’d start by rereading my note about rotating and scaling bitmaps, in case there was something there that might help fix it.

      • Bill Lahti says:

        I might have something new to add to this. In one of my apps, panning and zooming behaves pretty well. I will have to check the cod e there and see what I did to improve things. More later …

  4. Sagar says:

    Good project.. superb. its help me a lot.. for source code thanx…

  5. Pingback: More Mobile Enemy in Starship App | More Is Not Always Better

  6. nschl says:

    Great Tutorial, thank you for your work!
    I only found a little Problem and I wanted to ask you if you know a solution for this:
    In the “Canvas../Circle” Example there seems to be a Problem with panning/ zooming. Dragging the circle arround works fine, but if i try to zoom, the circle “jumps” across the screen to the other finger. If I release this finger, the circle jumps back to the other finger.

    Do you know this Problem? Is there any way to avoid it?

    • Bill Lahti says:

      That problem sounds familiar. With this sample app, I was progressing through a set of possible solutions, trying to see which one was best. Some of the techniques were not perfect, but I think I included them anyway. I have this vague memory of seeing that here or in another app and ending up at a better place by doing something with the gesture detector. Let me take a closer look at it and see what behavior I get on my devices.

      • nschl says:

        Thank you for your quick answer!
        I’ve been playing arround with it on my own, but i didn’t manage to find a solution. If you do, I would be really greatfull if you could share it.

      • Bill Lahti says:

        I definitely see what you are talking about. Panning works fine. I move the circle to the lower right. Then when I try to pinch-zoom the canvas, the circle shifts back toward the pinch-zoom point halfway between your fingers. Lift one finger and the circle jumps back. That leads me to believe that there is some confusion in the offset for the canvas origin. Panning has one origin and pinch-zoom has another. I have another demo where I combined translation and scaling (see Rotate Bitmap Example). I’m going to take a look at that and see if my more recent understanding is clearer than what was here in this multitouch example.
        I will post if that leads anywhere.

      • Bill Lahti says:

        I have not worked out anything yet. However, someone commented on this blog with a solution that might help.

  7. Mel says:

    Hi Bill,
    i’m trying to develop a zoom and pan function for my imageediting app which is my uni assignment. it has to function on an emulator hence no multitouch. so far i could implement a zoom function with the following code. though i am having difficulties integrating a pan function into this. i would probably have to use motion.event with ACTION_DOWN and ACTION_MOVE just don’t know how. thnks in advance!

    package com.example.ZoomApp;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.widget.ImageView;

    public class ZoomInOut extends ImageView
    Matrix matrix = new Matrix();
    float saveScale = 1f;
    float right, bottom, origWidth, origHeight, bmWidth, bmHeight;

    public ZoomInOut(Context context, AttributeSet attr){
    super(context, attr);
    matrix.setTranslate(1f, 1f);

    public void ZoomIn () {

    saveScale = saveScale + 0.1f;
    matrix.setScale( saveScale, saveScale );
    setImageMatrix( matrix );
    center(true, true);

    public void ZoomOut () {

    saveScale = saveScale – 0.1f;
    matrix.setScale( saveScale, saveScale );
    setImageMatrix( matrix );
    public void setImageBitmap(Bitmap bitmap){

    • Bill Lahti says:

      Sorry, I do not have any suggestions on what to do about multitouch when you are in the emulator. I did a quick search with Google, but nothing obviously easy was there. You might try some of the suggestions.

      • Mel says:

        hmm i guess iwasn’t very clear. i meant actually no multitouch. so it’s supposed to be a single touch zoom and pan with two buttons for zoom in and zoom out. but i’ve already partly solved the problem in the mean time.. though i have another problem. i want my zoom to be dependent on the pan. say, i zoom and make the image bigger and then i do panning . after the panning i want to zoom again. i want this to happen without the image bouncing back to the upper left corner, where it originally zooms when i click the zoom button for the first time, but rather at the position after panning. i hope i made myself clear.. am not sure how to describe it otherwise.
        for the panning i got the following ontouch method and thanks in advanc!

        public boolean onTouchEvent(MotionEvent event) {

        // Handle touch events here…
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
        start.set(event.getX(), event.getY());
        mode = DRAG;
        case MotionEvent.ACTION_POINTER_DOWN:
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_POINTER_UP:
        case MotionEvent.ACTION_MOVE:
        if (mode == DRAG) {
        // …
        matrix.postTranslate(event.getX() – start.x, event.getY()
        – start.y);


        return true;


      • Bill Lahti says:

        Another one of my posts might help you. See Rotate and Scale Bitmap Example. I wanted to understand scaling and rotating. It also included an offset so the scaling did not cause the image to move around.

  8. Hi, I’m working on an application for my master degree that handle with a large number of points (between 20k-50k) drawed with canvas.drawCircle(…). I have delay when I try to translate those points. Do you have any idea how to minimize this gap?

    All my code is based on this

    • Bill Lahti says:

      I have done a little with the canvas drawing methods in Android, but I am far from an expert. But I do have a few thoughts.

      I have a space war app in which I have a view with an onDraw method that draws a 30 x 30 grid on the screen. That’s 900 objects that get drawn on the screen and it goes fast enough that I can simulate motion of the starship at the center of the screen by translating the x-y origin. Each of the objects is drawn to the canvas with canvas.drawBitmap. If your circles are all the same and only vary in size (or if you had only N different circle types/colors), you might try drawing 20K circles with drawBitmap and see how the performance compares to what you do now. As I said, I do about 1K now and it’s fine. Hard to say what 20K bitmaps would do.

      Another thing to do is to look at how you calculate the dimensions and location of the circles. If you do a lot of computation in your onDraw method that would make it slow. Is there a way to move computation out of the drawing code?

      A year or so ago, I found a fun article and demo app about particle explosions. I don’t know how many particles get drawn, but you could take a look at that: Particle Explosions. Some of his comments about performance and the other articles he has written might help you.

      A good place to start for a discussion about Android performance might be the Chris Pruett talks and videos about Replica Island. All of that app makes use of SurfaceView so there are options to draw on the screen besides the onDraw of conventional View objects. I have not looked at his talks in a while, but think there might be a few points of interest for you. Links:



  9. Daniel says:

    I tried grabbing the example at the posted link on, but the file is returning a 415 when I try to download it. Is there any way for me to see the code for the pan-zoom?

    • Bill Lahti says:

      Use the download icon on the page. The icon is to the right of the date and the version number. That one works fine.
      When you click on the the zip file link, it goes to a Google Docs/Drive viewer for zip files. That way is not working.

  10. Pingback: Android Example of a Zoomable Game Board | More Is Not Always Better

  11. Shuhrat says:

    Hi, Is your app supports pan/zoom android TextureView?

  12. BubbaZ says:

    Hi Bill, Loved the article and am slowing making my way through this and your zoomable game board example. Thanks so much for sharing. I am stopped out currently on understanding how touch and zoom/pan interact. Specifically I have a ListView and whenever I zoom the touches seem to occur in the wrong places. I’ve posted to stack overflow here and put the code up in bit bucket here – If you should have time to look at either of those I would great appreciate any advice you could offer. Thanks for the articles and thanks in advance for any time you might be able to provide.

    • Bill Lahti says:

      I have not had a chance to look your code yet, but I do have a suggestion. You may have noticed that in some of my examples, I include a small white circle to indicate the point around which zooming occurs. I found that doing that was very helpful for debugging. It gave me something to look at and study the behavior of my code. That was much easier first step for debugging than debugging with breakpoints or reading output I wrote to the logcat window. So my suggestion for you is to add a circle or two, probably different colors, that show key properties of your zoomed list view. I have not thought much about what they would be. Perhaps, the touch point, the translated touch point, or maybe the center of a list item or two.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s