blob: 5828686b8828e20073090cc7e072a374a350cce5 [file] [log] [blame] [view]
David Morrisseya0bf8022014-05-30 14:08:31 +01001Subsampling Zoom Image View
2===========================
davemorrisseyb064b0b2013-08-26 09:45:26 -07003
David Morrisseyd4191862014-10-18 23:14:07 +01004Custom image views for Android with pinch to zoom, panning, rotation and animation support, with easy extension so you can add your own overlays and touch event detection.
5
David Morrissey5fa74e72014-07-31 00:11:06 +01006This library includes two classes, `ScaleImageView` and `SubsamplingScaleImageView`. `SubsamplingScaleImageView` is best for large images but doesn't support display of `Bitmap` objects or resources, and `ScaleImageView` supports `Bitmap` objects but not subsampling or large images. To decide which is best for you, see below.
David Morrissey365ccab2014-07-31 00:08:41 +01007
David Morrisseyef41d8a2014-06-10 01:00:09 +01008#### Download the sample app
9
10[![Get it on Google Play](https://developer.android.com/images/brand/en_generic_rgb_wo_60.png)](https://play.google.com/store/apps/details?id=com.davemorrissey.labs.subscaleview.sample)
11
David Morrisseyd4191862014-10-18 23:14:07 +010012#### Hall of fame
David Morrissey0e895c22013-08-26 20:07:35 +010013
David Morrisseyd4191862014-10-18 23:14:07 +010014**Are you using this library in your app? Let me know and I'll add it to this list.**
15
davemorrissey25acd202014-12-28 20:20:32 +000016| [![Fourth Mate](https://lh3.ggpht.com/2ALnL-05ILKLwP9U8Dfy7n4iI54OlXeZG-rHf31FP5l8Bup9wws9wnSlyX56ShgzlQ=w100)](https://play.google.com/store/apps/details?id=com.sleetworks.serenity.android) | [![Journey](https://lh3.ggpht.com/Mz6YqxKsLfVbjYVHj_3nfUxLe5Yvl9W4KO2sKnwud6hZl5mnGitm55PnILT2jx4Hafv6=w100)](https://play.google.com/store/apps/details?id=com.journey.app) | [![Clover](https://lh5.ggpht.com/Q8vw6LLyj3AjRev4ID3uvFUxnMp4ca4eBEaPlkupcK7cNn2xtVg-wIxVsKSJ-IIFaUM=w100)](https://play.google.com/store/apps/details?id=org.floens.chan) | [![Tag Gallery](https://lh5.ggpht.com/mKch3_fgPYswBPmZ-qEvp91_fPKdbvN2UubCvUTDqy1sAaLJBzfFYETb-sJgPfCvDg=w100)](https://play.google.com/store/apps/details?id=me.snapdiary.us.taggallery) |
17| ------------- | ------------- | ------------- | ------------- |
18| **Fourth Mate** | **Journal** | **Clover** | **Tag Gallery** |
David Morrisseyd4191862014-10-18 23:14:07 +010019
20#### About
David Morrissey365ccab2014-07-31 00:08:41 +010021
22`SubsamplingScaleImageView` uses subsampling and tiles to support large images. While zooming in, the
23low resolution, full size base layer is overlaid with smaller tiles at least as high resolution as the screen, and
24tiles are loaded and discarded during panning to avoid holding too much bitmap data in memory. This is ideal for use in image gallery apps where the size of the images may be large enough to require subsampling, and where
David Morrissey260aec22013-08-29 23:31:20 +010025pinch to zoom is required to view the high resolution detail.
26
David Morrissey365ccab2014-07-31 00:08:41 +010027*These views don't extend `ImageView` and aren't intended as a general purpose replacement for it. They're specialised for the display of photos and other large images, not the display of 9-patches, shapes and the other types of drawable that ImageView supports.*
David Morrissey0e895c22013-08-26 20:07:35 +010028
David Morrisseya0bf8022014-05-30 14:08:31 +010029#### Image display
David Morrissey365ccab2014-07-31 00:08:41 +010030
David Morrisseya0bf8022014-05-30 14:08:31 +010031* Display images from assets or the file system
32* Automatically rotate images from the file system (e.g. the camera or gallery) according to EXIF
33* Manually rotate images in 90° increments
34* Swap images at runtime
David Morrissey0e895c22013-08-26 20:07:35 +010035
David Morrissey365ccab2014-07-31 00:08:41 +010036*`SubsamplingScaleImageView` only:*
37
38* Display huge images, larger than can be loaded into memory
39* Show high resolution detail on zooming in
40* Tested up to 20,000x13,000px, though larger images are slower
41
42
David Morrisseya0bf8022014-05-30 14:08:31 +010043#### Gesture detection
44* One finger pan
45* Two finger pinch to zoom
46* Pan while zooming
47* Seamless switch between pan and zoom
48* Fling momentum after panning
David Morrissey77096ba2014-06-05 21:22:44 +010049* Double tap to zoom in and out
David Morrissey02ceb3d2014-05-30 20:48:51 +010050* Options to disable pan and/or zoom gestures
David Morrisseya0bf8022014-05-30 14:08:31 +010051
David Morrissey9f3fad12014-06-08 10:16:49 +010052#### Animation
53* Public methods for animating the scale and center
54* Customisable duration and easing
55* Optional uninterruptible animations
56
David Morrisseya0bf8022014-05-30 14:08:31 +010057#### Overridable event detection
58* Supports `OnClickListener` and `OnLongClickListener`
59* Supports interception of events using `GestureDetector` and `OnTouchListener`
60* Extend to add your own gestures
61
62#### Easy integration
63* Use within a `ViewPager` to create a photo gallery
64* Easily restore scale, center and orientation after screen rotation
65* Can be extended to add overlay graphics that move and scale with the image
66* Handles view resizing and `wrap_content` layout
67
David Morrisseya0bf8022014-05-30 14:08:31 +010068#### Limitations
David Morrissey365ccab2014-07-31 00:08:41 +010069* `SubsamplingScaleImageView` requires SDK 10 (Gingerbread).
70* `SubsamplingScaleImageView` cannot decode an image from resources or display a `Bitmap` object - the image file needs to be in assets or external storage.
David Morrissey02e7dd22014-11-19 21:42:22 +000071* `SubsamplingScaleImageView` cannot display grayscale PNGs on Android Lollipop, due to bugs in the skia library and/or BitmapRegionDecoder. Earlier versions of Android also have issues displaying some grayscale PNGs, but not all. I have reported these bugs to Google.
David Morrissey365ccab2014-07-31 00:08:41 +010072* These views do not extend ImageView so attributes including android:tint, android:scaleType and android:src are not supported.
David Morrisseya0bf8022014-05-30 14:08:31 +010073* Images stored in assets cannot be rotated based on EXIF, you'll need to do it manually. You probably know the orientation of your own assets :-)
74
David Morrissey365ccab2014-07-31 00:08:41 +010075## Which view is best?
76
77Use `SubsamplingScaleImageView` if:
78
79* You want to zoom into very large images without losing detail.
80* You need to display images of unknown size e.g. from the camera or gallery.
81* You don't know if the images may be too large to fit in memory on some devices.
82* You need to display images larger than 2048px.
83* You don't need to support devices older than SDK 10.
84
85Use `ScaleImageView` if:
86
87* You know the size of the images you're displaying.
88* You know the images are small enough to fit in memory on all your target devices.
89* Your images are no larger than 2048px, or you are able to scale them down.
90* You need to support devices older than SDK 10.
91
David Morrissey8e742a72014-06-06 20:23:03 +010092## Quality notes
93
94Images are decoded as dithered RGB_565 bitmaps by default, because this requires half as much memory as ARGB_8888. For most
95JPGs you won't notice the difference in quality. If you are displaying large PNGs with alpha channels, Android will probably
96decode them as ARGB_8888, and this may cause `OutOfMemoryError`s. **If possible, remove the alpha channel from PNGs larger than about 2,000x2,000.**
97This allows them to be decoded as RGB_565.
98
David Morrisseya0bf8022014-05-30 14:08:31 +010099## Basic setup
100
David Morrissey2b6553c2015-01-09 00:05:43 +0000101Add the library to your app using one of these methods:
David Morrissey64f2ef02014-11-15 15:39:35 +0000102
David Morrissey2b6553c2015-01-09 00:05:43 +0000103* Add `com.davemorrissey.labs:subsampling-scale-image-view:2.2.1` as a dependency in your build.gradle file
104* *or* download the library aar file from the releases page and add to your app manually
105* *or* clone the project and import the library subproject as a module in your app
David Morrissey64f2ef02014-11-15 15:39:35 +0000106* *or* clone the project and copy the resources and classes from `com.davemorrissey.labs.subscaleview` into your project
David Morrisseya0bf8022014-05-30 14:08:31 +0100107
108Add the view to your layout XML as shown below. Normally you should set width and height to `match_parent`.
109
110 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
111 android:layout_width="match_parent"
112 android:layout_height="match_parent" >
113
114 <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
115 android:id="@+id/imageView"
David Morrisseye11ee3e2014-05-30 14:09:41 +0100116 android:layout_width="match_parent"
117 android:layout_height="match_parent"/>
David Morrisseya0bf8022014-05-30 14:08:31 +0100118
119 </RelativeLayout>
120
121Now, in your fragment or activity, set the image asset name or file path.
122
123 SubsamplingScaleImageView imageView = (SubsamplingScaleImageView)findViewById(id.imageView);
124 imageView.setImageAsset("map.png");
125 // ... or ...
126 imageView.setImageFile("/sdcard/DCIM/DSCM00123.JPG");
127
128That's it! Keep reading for some more options.
129
130## Define asset name in XML
131
132For a zero code approach to showing an image from your assets, you need to define the custom namespace in your layout.
133
134 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
135 xmlns:ssiv="http://schemas.android.com/apk/res-auto"
136 android:layout_width="match_parent"
137 android:layout_height="match_parent" >
138
139 <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
140 ssiv:assetName="map.png"
David Morrisseye11ee3e2014-05-30 14:09:41 +0100141 android:layout_width="match_parent"
142 android:layout_height="match_parent"/>
David Morrisseya0bf8022014-05-30 14:08:31 +0100143
144 </RelativeLayout>
145
146**This method doesn't support restoring state after a screen orientation change.**
147
148## Handle screen orientation changes
149
150If you want the current scale, center and orientation to be preserved when the screen is rotated, you can request it from the view's `getState` method, and restore it after rotation, by passing it to the view along with the image asset name or file path. Here's a simple example of how you might do this in a fragment.
151
152 private static final String BUNDLE_STATE = "ImageViewState";
153
154 @Override
155 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
156 View rootView = inflater.inflate(R.layout.my_fragment, container, false);
157
158 ImageViewState imageViewState = null;
159 if (savedInstanceState != null && savedInstanceState.containsKey(BUNDLE_STATE)) {
160 imageViewState = (ImageViewState)savedInstanceState.getSerializable(BUNDLE_STATE);
161 }
162 SubsamplingScaleImageView imageView = (SubsamplingScaleImageView)rootView.findViewById(id.imageView);
163 imageView.setImageAsset("map.png", imageViewState);
164
165 return rootView;
166 }
167
168 @Override
169 public void onSaveInstanceState(Bundle outState) {
170 View rootView = getView();
171 if (rootView != null) {
172 SubsamplingScaleImageView imageView = (SubsamplingScaleImageView)rootView.findViewById(id.imageView);
173 ImageViewState state = imageView.getState();
174 if (state != null) {
175 outState.putSerializable(BUNDLE_STATE, imageView.getState());
176 }
177 }
178 }
179
180## Extending functionality
181
David Morrissey365ccab2014-07-31 00:08:41 +0100182Take a look at the sample app for examples of classes that overlay graphics on top of the image so that they move and scale with it. `FreehandView` adds event detection, capturing only the touch events it needs so pan and zoom still work normally.
David Morrissey5833e302014-06-06 22:08:14 +0100183
184## About
185
186Copyright 2014 David Morrissey, and licensed under the Apache License, Version 2.0. No attribution is necessary but it's very much appreciated. Star this project to show your gratitude.
187
davemorrissey25acd202014-12-28 20:20:32 +0000188This project started life as a way of showing very large images (e.g. a large building floor plan) with gestures to pan and zoom, with support for extensions that showed overlays (location pins, annotations) aligned with the image. It's grown massively, but for the moment I am keeping everything in one class to prevent subclasses and extensions breaking the assumptions (or violating invariants) on which the class depends.