PhotoView v1.1

As you may or not know, I released a small library as part of the work I did for photup (it was actually created before that) which contained an ImageView which could be scaled, dragged and double-tapped. Even though I released it onto GitHub about 2 months ago, I haven’t really maintained it since. Until now. I’ve spent a good few days improving it, making it better and easier to use, and I think I’ve achieved both in today’s release: v1.1.

Sample Application

I’ve uploaded the sample app to Google Play, meaning you can have a play with it easily.
Download on Google Play

Improving it

I’m not going to go too much into the technical details of how PhotoView has improved (you can look at the GitHub commit log for that), but I’ll outline some of the big points:

  • PhotoView was previously a bit flaky when used in scrolling ViewGroups, especially horizontally scrolling ones such as ViewPager. This is no longer the case in v1.1, and PhotoView now handles this with ease. For instance, the ViewPager won’t scroll unless you’re at the edge of the Drawable, just as you expect.
  • To my shame, PhotoView wasn’t previously using a touch slop (a low-pass on drag events) so it would sometimes jump around a lot. v1.1 fixes this.
  • There are quite a few other fixes and improvements in there but I won’t bore you with the details.

Easier to use

Previously PhotoView was a class which extended from ImageView, this allowed very easy access to the callbacks it needed (such as onTouchEvent(), setImageDrawable() & setFrame()). While it was easy for me to implement it this way, it’s not so easy for developers to actually use it. I’d be surprised if you don’t have a large hierarchy of ImageView derivatives in your projects already, so adding another into the mix isn’t very useful at all.

Thus, in v1.1, I decided to refactor the library so that it can work with any* ImageView easily, and the result is PhotoViewAttacher. The usage of PhotoViewAttacher is actually very simple:

ImageView mImageView;
PhotoViewAttacher mAttacher;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Any implementation of ImageView can be used!
    mImageView = (ImageView) findViewById(R.id.iv_photo);

    // Set the Drawable displayed
    Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper);
    mImageView.setImageDrawable(bitmap);

    // Attach a PhotoViewAttacher, which takes
    // care of all of the zooming functionality.
    mAttacher = new PhotoViewAttacher(mImageView);
}

// If you later call mImageView.setImageDrawable, etc
// then you just need to call
attacher.update();

Doesn’t get much simpler than that! What actually happens when you create a PhotoViewAttacher is that it attaches a number of listeners to the ImageView:

  • OnTouchListener. Pretty obvious why it needs this, so it can react to any touch events.
  • OnGlobalLayoutListener. This isn’t a very well known listener (and for good reason) but it allows PhotoViewAttacher to be notified whenever any of the Views in the hierarchy changes. We don’t care about other Views but we need this to be able to react if the ImageView’s bounds change. Ideally I would have used OnLayoutChangeListener but that’s only available from API level 11+.

And that’s it, everything else in the library is concerned with handling touch events, so have a dig in the code!
*any ImageView that doesn’t already have a View.OnTouchListener set on it

7 Comments.
  1. Jovian Chen says:

    Incredible job! Could you tell me where can I found the github link?

  2. ak says:

    i am using your library. it takes care of all the cases. the one problem that i have is in the photoViewAttacher , the fling doesnt happen if the image is too big.

  3. chrisbanes says:

    What is too big? How big is the image you’re displaying? Device screen size?

  4. ak says:

    sorry for not being clear.

    1. The images are in a ViewPager. the problem comes when this image comes, after which I cant fling to the next/previous image. However, when I zoom in this picture I can now fling to the next/previous image.

    2. I captured the log in fling() in FlingRunnable

    rect.width(): 369.6047
    rect.height(): 762.0

    mImageView.getWidth() :480
    mImageView.getHeight() :762

    3. Heres the log at the image
    D/PhotoViewAttacher(29827): onFling. sX: 497.82916 sY: 489.89648 Vx: -2282.9492 Vy: -312.97806
    D/PhotoViewAttacher(29827): fling. StartX:-55 StartY:0 MaxX:-55 MaxY:0

    thank you for your time and help

  5. chrisbanes says:

    For the values you just pasted means that the image is centered perfectly, so can’t fling.

  6. waqas ahmed says:

    How can I add multiple images on a single Activity and zoom, move and pinch images?

Comments have been disabled.