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.
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