Implements pinch-zoom, rotate, pan as an ImageView for Android 2.1+

Last update: Aug 13, 2022

GestureImageView

This is a simple Android View class which provides basic pinch and zoom capability for images.

Can be used as a replacement for a standard ImageView when you want to include pinch and zoom.

UPDATE (Aug 2012)

Just a quick note to all those who have posted issues recently, I HAVE seen them but just haven't had a chance to fix them. I'll do my best to get to them ASAP. Thanks for being patient

Features:

  1. Pinch zoom in place (i.e. zoom occurs from point of touch)
  2. Panning with fling gesture
  3. Double tap zoom
  4. Configurable zoom boundaries (min/max)

Limitations:

  1. Does not support Rotation
  2. Does not support Pan and Zoom together
  3. Not all methods of ImageView class are supported (will throw UnsupportedOperationException if strict is true)

What's New in the Latest Release!

  • Added double-tap zoom support
  • Added support for onClickListener
  • Added support for non-image drawables

New in previous releases

  • Added support for CENTER, CENTER_INSIDE and CENTER_CROP scale types
  • Added support for custom OnTouchListener on GestureImageView
  • Fixed NPE when no drawable set on view
  • Fixed/improved calculation of scale limits

Usage

Notes:

  1. Setting gesture-image:strict to true will result in UnsupportedOperationException if an unsupported method is called.
  2. Setting gesture-image:recycle to true will automatically reycle bitmap images when the view is destroyed.

Configured as View in layout.xml

code:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:gesture-image="http://schemas.polites.com/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <com.polites.android.GestureImageView
        android:id="@+id/image"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/image"
        gesture-image:min-scale="0.1"
        gesture-image:max-scale="10.0"
        gesture-image:strict="false"/>

</LinearLayout>

Configured Programmatically

code:

import com.polites.android.GestureImageView;

import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.LinearLayout.LayoutParams;

public class SampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

        GestureImageView view = new GestureImageView(this);
        view.setImageResource(R.drawable.image);
        view.setLayoutParams(params);

        ViewGroup layout = (ViewGroup) findViewById(R.id.layout);

        layout.addView(view);
    }
}

License

Apache License 2.0

GitHub

https://github.com/jasonpolites/gesture-imageview
Comments
  • 1. Change picture of GestureImageView does not work.

    setImageBitmap/setImageDrawable/setImageResource does not work when call continuously.

    These functions works very well if you call only one time. But if you call more than two time, the image disappeared.

    Reviewed by keppelcao at 2012-05-19 11:24
  • 2. Need help setting scale programmatically

    Hi there and thanks so much for taking your time to help others and sharing your hard work. I am still quite new to android programming and I know this is probably silly and obvious, but how do I set the images zoom level on the first load if I wanted it to be zoomed to a certain location in the image at a particular scale? I've tried setscale(XXX) with different values for XXX and have tried to use the redraw() method (along with any others I thought might do it) but I can't figure it out. I know you're working to update the project tonight and I don't want to slow you down, but I'd appreciate it if you could help me out with this simple task (*doh) when you have a second. Thank you!

    Reviewed by winryan at 2012-05-08 22:15
  • 3. Zooming out happens in-place then snaps back to top-left corner

    When zooming out on an image that does not take up the whole View, pinching will render the image smaller, in-place where I pinched it. After I let go, it snaps to the top-left corner of the view.

    Can you tell I'm currently implementing stuff with this code? ;)

    Expected behavior would be either:

    -Once the scaled image size < view size, leave the top-left corner anchored and only zoom the bottom right corner toward it, or -Leave the image where I pinched it, offset from the corner

    Reviewed by jcogilvie at 2012-04-01 20:38
  • 4. Using drawable resource is not supported

    Hello Jason,

    I changed from a simple drawable to a drawable resource

    the result was that nothing was drawn but also there weren't any errors.

    This is the setup: main.xml

        <com.polites.android.GestureImageView
            android:id="@+id/my_id"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="0.9"
            android:adjustViewBounds="true"
            android:src="@drawable/simple_layer" />
    

    drawable/simple_layer.xml

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
        <!-- <item -->
        <!-- android:id="@[+][package:]id/resource_name" -->
        <!-- android:bottom="dimension" -->
        <!-- android:drawable="@[package:]drawable/drawable_resource" -->
        <!-- android:left="dimension" -->
        <!-- android:right="dimension" -->
        <!-- android:top="dimension"/> -->
    
        <item android:src="@drawable/img1"/>
        <item android:src="@drawable/img2"/>
    </layer-list>
    

    I checked onDraw, onLayout and on onMessuere by directing directly to the parent imageview but in all cases id didn't change the result.

    Do you have any ideas what it might be that caused not to display the drawables?

    Reviewed by Vad1mo at 2012-05-08 20:53
  • 5. Changing bitmaps requires another action before new bitmap displays

    I have a loading image that I use before my actual image is download. If I call setBitmap after the first image is already displayed, nothing happens. I suspect you should call redraw() at the end of setBitmap and setResource.

    Reviewed by jcogilvie at 2012-04-01 20:03
  • 6. Preserve ImageView inheritance hierarchy?

    I'm trying to make use of this library, which is exactly what I was looking for; but there are a million places in my code where I refer to ImageViews as ImageViews specifically, and since a GestureImageView doesn't extend ImageView, I need to create a whole bunch of overloaded function calls in order to continue using my existing code base.

    I'm wondering why you've chosen not to extend ImageView.

    For reference, there's this old project here (http://code.google.com/p/android-pinch/) that does extend ImageView and provides similar functionality. I don't know if it will be of use to you or if you've seen it before.

    Reviewed by jcogilvie at 2012-04-01 19:02
  • 7. zoom control buttons

    please could you add zoom control buttons for devices that do not support multi touch (there are a few running android 2.2) they can be either the zoom controls provided within android or custom zoom in and out buttons.

    any help asap would be appreciated thanks

    Reviewed by tuffyg at 2012-03-08 13:27
  • 8. Making Animations

    Hello, I am modifying the double tap listener to use for zooming in and zooming out. I was wondering how to integrate my simple ZoomAnimation.

    
        public boolean update(GestureImageView view, long elapsedTime) {
    
            if (elapsedTime<duration){
                return true;
            }
            return false;
        }
    

    Now I have to move and scale the image in the direction where the touch event occurred. How can I reuse your code to do so.

            if(doubleTapDetector.onTouchEvent(event)) {
                initialDistance = 0;
                lastScale = currentScale;
                //lastScale = image.getScale();;
                currentScale = image.getScale();;
                next.x = image.getImageX();
                next.y = image.getImageY();
                calculateBoundaries();
                image.animationStart(new ZoomAnimation());
            }
    

    would you mind to give some hints on doing so reusing your code.

    Thank you!

    Reviewed by Vad1mo at 2012-05-03 09:14
  • 9. Add onTouchOutsideDrawable() to GestureImageViewListener

    We use our GestureImageView on a dialog and we wanted to be able to dismiss the dialog by tapping on the "empty" area of the ImageView.

    This adds the ability to detect if the touch event happened outside of the Drawable associated with the GestureImageView.

    Cheers!

    Reviewed by goncalossilva at 2012-04-16 01:04
  • 10. Portrait orientation starts out full-size on too-tall images

    Hi there! Again. :)

    I have an image that's bigger vertically than horizontally (as an example, a screenshot of an average-sized android device while sitting in portrait orientation, displayed on said device).

    When I put the device into landscape mode showing the same image, it's scaled properly (initially displays a zoomed-out image on the screen, but the canvas takes up the whole available width).

    When I put it into portrait mode, if I have any header or footer on the page, the image shows up zoomed in to fit its width on the screen, at the expense of cutting off the height.

    Perhaps there's a way to implement the android:scaleType attribute? This may come as two different attributes for this view: one to indicate the scale type for the view bounds (i.e., whether the canvas should wrap to the initial sizing of the image) and one to indicate the initial display.

    I could for example set the scaleType to fitCenter, and then choose whether I want the canvas to take all available space so I can zoom to full size, or I want it to collapse the canvas down to wrap the initial image.

    Reviewed by jcogilvie at 2012-04-04 23:56
  • 11. Is there a way to zoom buttons on top of the GestureImageView?

    I have a series of buttons on a graphic which are positioned in order that they correspond to a part of the graphic. When I zoom in or out, the buttons stay where they were. Is there a way to make them zoom with the background or is there a function in GestureImageView that would let me read the parameters by which to move them (x,y) position and height/width?

    Reviewed by eddiekeane at 2012-07-04 13:16
  • 12. A possible fix for dynamic image loading

    Original version: protected void initImage() { if(this.drawable != null) { this.drawable.setAlpha(alpha); this.drawable.setFilterBitmap(true); if(colorFilter != null) { this.drawable.setColorFilter(colorFilter); } } if(!layout) { requestLayout(); redraw(); } } Fixed version: protected void initImage() { if(this.drawable != null) { this.drawable.setAlpha(alpha); this.drawable.setFilterBitmap(true); if(colorFilter != null) { this.drawable.setColorFilter(colorFilter); } } if(!layout) { requestLayout(); } else { drawable.setBounds(-hWidth,-hHeight,hWidth,hHeight); } redraw(); }

    Reviewed by heagoo at 2018-04-06 10:11
  • 13. Reusing the View fixed

    @jasonpolites I applied the fix proposed in https://github.com/jasonpolites/gesture-imageview/issues/64 and tested it in one of my projects. It is working fine, so I'm glad to offer you a pull request.

    Reviewed by Raegdan at 2015-09-16 11:41
  • 14. Solve gesture-imageview in viewpage can't Drag

    Don't know good place where i should post this method, Holp to help some people.

    In GestureImageViewTouchListener.java file, locate Ontouch methed.

    @Override public boolean onTouch(View v, MotionEvent event) {

        Log.i("", "-----onTouch-----" + inZoom);
    
        if (!inZoom) {
    
            if (!tapDetector.onTouchEvent(event)) {
                if (event.getPointerCount() == 1 && flingDetector.onTouchEvent(event)) {
                    startFling();
                }
    
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    handleUp();
                } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    stopAnimations();
    
                    last.x = event.getX();
                    last.y = event.getY();
    
                    if (boundaryLeft != last.x) {
                        v.getParent().requestDisallowInterceptTouchEvent(true);
                    }
    
                    if (imageListener != null) {
                        imageListener.onTouch(last.x, last.y);
                    }
    
                    touched = true;
                } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    if (event.getPointerCount() > 1) {
                        multiTouch = true;
                        if (initialDistance > 0) {
    
                            pinchVector.set(event);
                            pinchVector.calculateLength();
    
                            float distance = pinchVector.length;
    
                            if (initialDistance != distance) {
    
                                float newScale = (distance / initialDistance) * lastScale;
    
                                if (newScale <= maxScale) {
                                    scaleVector.length *= newScale;
    
                                    scaleVector.calculateEndPoint();
    
                                    scaleVector.length /= newScale;
    
                                    float newX = scaleVector.end.x;
                                    float newY = scaleVector.end.y;
    
                                    handleScale(newScale, newX, newY);
                                }
                            }
                        } else {
                            initialDistance = MathUtils.distance(event);
    
                            MathUtils.midpoint(event, midpoint);
    
                            scaleVector.setStart(midpoint);
                            scaleVector.setEnd(next);
    
                            scaleVector.calculateLength();
                            scaleVector.calculateAngle();
    
                            scaleVector.length /= lastScale;
                        }
                    } else {
    
    
                        if (!touched) {
                            touched = true;
                            last.x = event.getX();
                            last.y = event.getY();
                            next.x = image.getImageX();
                            next.y = image.getImageY();
                        } else if (!multiTouch) {
    
                            Log.i("", "x:" + next.x + " multiTouch:" + boundaryRight
                                    + "  " + boundaryLeft);
                            if ((boundaryRight / 2) == next.x) { // nomal model
                                v.getParent().requestDisallowInterceptTouchEvent(false);
                            } else {
                                if (next.x > 0 && boundaryRight > 0 && boundaryLeft > 0) {   //double model
    
                                    v.getParent().requestDisallowInterceptTouchEvent(  //disable parant event
                                            false);
                                } else if (next.x != boundaryLeft && next.x < 0) {    //right boundary  on srceen
                                    v.getParent()
                                            .requestDisallowInterceptTouchEvent(true);
                                } else if (next.x != boundaryRight && next.x > 0) {   //left boundary  on srceen
                                    v.getParent()
                                            .requestDisallowInterceptTouchEvent(true);  
                                } else {
                                    v.getParent().requestDisallowInterceptTouchEvent(
                                            false);
                                }
    
                            }
    
                            if (handleDrag(v, event.getX(), event.getY())) {
                                image.redraw();
                            }
                        }
                    }
                }
            }
        } 
    
    Reviewed by canven at 2015-06-11 06:34
  • 15. Support cropping of Bitmap in GestureImageView

    Hello,

    It would be great if in GestureImageView, there could be a function, say getCroppedImage(), where it will return a cropped bitmap.

    This cropped bitmap will be whatever is displayed in the ImageView after the user has scaled/translated the Bitmap in the GestureImageView.

    Basically, I'm trying to recreate how an image is displayed in an ImageView that is part of a separate layout than the GestureImageView, so some how I must save the GestureImageView's bitmap state.

    Reviewed by dejesus2010 at 2015-05-07 20:46
Big image viewer supporting pan and zoom, with very little memory usage and full featured image loading choices. Powered by Subsampling Scale Image View, Fresco, Glide, and Picasso. Even with gif and webp support! 🍻
Big image viewer supporting pan and zoom, with very little memory usage and full featured image loading choices. Powered by Subsampling Scale Image View, Fresco, Glide, and Picasso. Even with gif and webp support! 🍻

BigImageViewer Big image viewer supporting pan and zoom, with very little memory usage and full featured image loading choices. Powered by Subsampling

Aug 17, 2022
Customizable Android full screen image viewer for Fresco library supporting "pinch to zoom" and "swipe to dismiss" gestures. Made by Stfalcon
Customizable Android full screen image viewer for Fresco library supporting

This project is no longer supported. If you're able to switch from Fresco to any other library that works with the Android's ImageView, please migrate

Aug 6, 2022
Tutorial Double Tap Pinch to Zoom with kotlin
Tutorial Double Tap Pinch to Zoom with kotlin

Double-Tap-Pinch-Zoom Tutorial Double Tap Pinch to Zoom Tutorial Build with Andr

Apr 13, 2022
PinchToZoom - Pinch to zoom used within list like Instagram

Pinch To Zoom ?? Description Pinch to Zoom with Pan Gestures like Instagram ?? Motivation and Context Big Thanks ???? to the guy and his amazing repo

Apr 12, 2022
Android library (AAR). Highly configurable, easily extendable deep zoom view for displaying huge images without loss of detail. Perfect for photo galleries, maps, building plans etc.
Android library (AAR). Highly configurable, easily extendable deep zoom view for displaying huge images without loss of detail. Perfect for photo galleries, maps, building plans etc.

Subsampling Scale Image View A custom image view for Android, designed for photo galleries and displaying huge images (e.g. maps and building plans) w

Aug 13, 2022
Slider-Gallery-Zoom: image slider for android supporting indicator and auto scroll with clicking on image

image slider supporting indicator and auto scroll with clicking on image to open full screen image slider swipe and pinch zoom gestures like gallery,just pass your images and the position of the current image.

May 28, 2022
Custom ImageView for android with polygon shape (Android)
Custom ImageView for android with polygon shape (Android)

PolygonImageView Create a custom ImageView with polygonal forms. Usage To use PolygonImageView, add the module into your project and start to build xm

Jun 3, 2022
Implementation of ImageView for Android that supports zooming, by various touch gestures.
Implementation of ImageView for Android that supports zooming, by various touch gestures.

PhotoView PhotoView aims to help produce an easily usable implementation of a zooming Android ImageView. [ Dependency Add this in your root build.grad

Aug 14, 2022
A circular ImageView for Android
A circular ImageView for Android

CircleImageView A fast circular ImageView perfect for profile images. This is based on RoundedImageView from Vince Mi which itself is based on techniq

Mar 29, 2021
Adds touch functionality to Android ImageView.

TouchImageView for Android Capabilities TouchImageView extends ImageView and supports all of ImageView’s functionality. In addition, TouchImageView ad

Mar 28, 2021
Custom shaped android imageview components
Custom shaped android imageview components

Shape Image View Provides a set of custom shaped android imageview components, and a framework to define more shapes. Implements both shader and bitma

Mar 29, 2021
Android ImageView replacement which allows image loading from URLs or contact address book, with caching

Smart Image View for Android SmartImageView is a drop-in replacement for Android’s standard ImageView which additionally allows images to be loaded fr

Jul 30, 2022
Android ImageView that handles animated GIF images
Android ImageView that handles animated GIF images

GifImageView Android ImageView that handles Animated GIF images Usage In your build.gradle file: dependencies { compile 'com.felipecsl:gifimageview:

Mar 9, 2021
Android ImageView that supports different radii on each corner.
Android ImageView that supports different radii on each corner.

SelectableRoundedImageView Note that this project is no longer maintained. Android ImageView that supports different radii on each corner. It also sup

Mar 17, 2021
ImageView with a tag on android
ImageView with a tag on android

SimpleTagImageView ImageView with a tag in android. So it's a ImageView. Demo ####Warning:When you set the round radius,the simpletagimageview scale t

Jun 30, 2022
Custom ImageView for moving image around the screen (Android)
Custom ImageView for moving image around the screen (Android)

MovingImageView Create a custom ImageView for moving image around the screen. Usage To use MovingImageView, add the module into your project and start

Aug 8, 2022
Flickable ImageView for Android. It's like a view of twitter's detail image.
Flickable ImageView for Android. It's like a view of twitter's detail image.

FlickableView Flickable ImageView for Android. It's like a view of twitter's detail image. It's possible that other views animate with FlickableView.

Sep 13, 2021
Create parallax and any other transformation effects on scrolling android ImageView
Create parallax and any other transformation effects on scrolling android ImageView

Android Parallax Image View Creates effect such as vertical parallax, horizontal parallax etc. on android ImageView when it's being vertically or hori

Jul 7, 2022
Implementation of ImageView for Android that supports zooming, by various touch gestures.
Implementation of ImageView for Android that supports zooming, by various touch gestures.

PhotoView PhotoView aims to help produce an easily usable implementation of a zooming Android ImageView. [ Dependency Add this in your root build.grad

Aug 17, 2022