blob: fcd131c228a46d16fb33fd3f02207566978e3336 [file] [log] [blame] [view]
David Morrisseya0bf8022014-05-30 14:08:31 +01001Subsampling Zoom Image View
2===========================
davemorrisseyb064b0b2013-08-26 09:45:26 -07003
David Morrissey0e895c22013-08-26 20:07:35 +01004A custom ImageView for Android with pinch to zoom and subsampled tiles to support large images. While zooming in, the
David Morrisseya0bf8022014-05-30 14:08:31 +01005low resolution, full size base layer is overlaid with smaller tiles at least as high resolution as the screen, and
David Morrissey260aec22013-08-29 23:31:20 +01006tiles are loaded and discarded during panning to avoid holding too much bitmap data in memory.
David Morrissey0e895c22013-08-26 20:07:35 +01007
David Morrissey260aec22013-08-29 23:31:20 +01008Ideal for use in image gallery apps where the size of the images may be large enough to require subsampling, and where
9pinch to zoom is required to view the high resolution detail.
10
David Morrisseya0bf8022014-05-30 14:08:31 +010011*This view doesn't extend `ImageView` and isn't intended as a general replacement for it. It's specialized for the display of much larger images than `ImageView` can display, so it's perfect for image galleries and displaying images from the camera.*
David Morrissey0e895c22013-08-26 20:07:35 +010012
David Morrisseya0bf8022014-05-30 14:08:31 +010013#### Image display
14* Display huge images, larger than can be loaded into memory
15* Show high resolution detail on zooming in
16* Tested up to 20,000x13,000px, though larger images are slower
17* Display images from assets or the file system
18* Automatically rotate images from the file system (e.g. the camera or gallery) according to EXIF
19* Manually rotate images in 90° increments
20* Swap images at runtime
David Morrissey0e895c22013-08-26 20:07:35 +010021
David Morrisseya0bf8022014-05-30 14:08:31 +010022#### Gesture detection
23* One finger pan
24* Two finger pinch to zoom
25* Pan while zooming
26* Seamless switch between pan and zoom
27* Fling momentum after panning
David Morrissey77096ba2014-06-05 21:22:44 +010028* Double tap to zoom in and out
David Morrissey02ceb3d2014-05-30 20:48:51 +010029* Options to disable pan and/or zoom gestures
David Morrisseya0bf8022014-05-30 14:08:31 +010030
31#### Overridable event detection
32* Supports `OnClickListener` and `OnLongClickListener`
33* Supports interception of events using `GestureDetector` and `OnTouchListener`
34* Extend to add your own gestures
35
36#### Easy integration
37* Use within a `ViewPager` to create a photo gallery
38* Easily restore scale, center and orientation after screen rotation
39* Can be extended to add overlay graphics that move and scale with the image
40* Handles view resizing and `wrap_content` layout
41
42#### Coming soon...
David Morrisseya0bf8022014-05-30 14:08:31 +010043* Better support for very tall or wide images
David Morrisseya0bf8022014-05-30 14:08:31 +010044* Demonstration app
45
46#### Limitations
David Morrissey0e895c22013-08-26 20:07:35 +010047* Requires SDK 10 (Gingerbread).
David Morrisseya0bf8022014-05-30 14:08:31 +010048* No support for decoding an image from resources - the image file needs to be in assets or external storage.
David Morrissey260aec22013-08-29 23:31:20 +010049* This view does not extend ImageView so attributes including android:tint, android:scaleType and android:src are not supported.
David Morrisseya0bf8022014-05-30 14:08:31 +010050* 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 :-)
51
David Morrissey8e742a72014-06-06 20:23:03 +010052## Quality notes
53
54Images are decoded as dithered RGB_565 bitmaps by default, because this requires half as much memory as ARGB_8888. For most
55JPGs you won't notice the difference in quality. If you are displaying large PNGs with alpha channels, Android will probably
56decode them as ARGB_8888, and this may cause `OutOfMemoryError`s. **If possible, remove the alpha channel from PNGs larger than about 2,000x2,000.**
57This allows them to be decoded as RGB_565.
58
David Morrisseya0bf8022014-05-30 14:08:31 +010059## Basic setup
60
61Checkout the project and import the library project as a module in your app. Alternatively you can just copy the classes in `com.davemorrissey.labs.subscaleview` to your project.
62
63Add the view to your layout XML as shown below. Normally you should set width and height to `match_parent`.
64
65 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
66 android:layout_width="match_parent"
67 android:layout_height="match_parent" >
68
69 <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
70 android:id="@+id/imageView"
David Morrisseye11ee3e2014-05-30 14:09:41 +010071 android:layout_width="match_parent"
72 android:layout_height="match_parent"/>
David Morrisseya0bf8022014-05-30 14:08:31 +010073
74 </RelativeLayout>
75
76Now, in your fragment or activity, set the image asset name or file path.
77
78 SubsamplingScaleImageView imageView = (SubsamplingScaleImageView)findViewById(id.imageView);
79 imageView.setImageAsset("map.png");
80 // ... or ...
81 imageView.setImageFile("/sdcard/DCIM/DSCM00123.JPG");
82
83That's it! Keep reading for some more options.
84
85## Define asset name in XML
86
87For a zero code approach to showing an image from your assets, you need to define the custom namespace in your layout.
88
89 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
90 xmlns:ssiv="http://schemas.android.com/apk/res-auto"
91 android:layout_width="match_parent"
92 android:layout_height="match_parent" >
93
94 <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
95 ssiv:assetName="map.png"
David Morrisseye11ee3e2014-05-30 14:09:41 +010096 android:layout_width="match_parent"
97 android:layout_height="match_parent"/>
David Morrisseya0bf8022014-05-30 14:08:31 +010098
99 </RelativeLayout>
100
101**This method doesn't support restoring state after a screen orientation change.**
102
103## Handle screen orientation changes
104
105If 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.
106
107 private static final String BUNDLE_STATE = "ImageViewState";
108
109 @Override
110 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
111 View rootView = inflater.inflate(R.layout.my_fragment, container, false);
112
113 ImageViewState imageViewState = null;
114 if (savedInstanceState != null && savedInstanceState.containsKey(BUNDLE_STATE)) {
115 imageViewState = (ImageViewState)savedInstanceState.getSerializable(BUNDLE_STATE);
116 }
117 SubsamplingScaleImageView imageView = (SubsamplingScaleImageView)rootView.findViewById(id.imageView);
118 imageView.setImageAsset("map.png", imageViewState);
119
120 return rootView;
121 }
122
123 @Override
124 public void onSaveInstanceState(Bundle outState) {
125 View rootView = getView();
126 if (rootView != null) {
127 SubsamplingScaleImageView imageView = (SubsamplingScaleImageView)rootView.findViewById(id.imageView);
128 ImageViewState state = imageView.getState();
129 if (state != null) {
130 outState.putSerializable(BUNDLE_STATE, imageView.getState());
131 }
132 }
133 }
134
135## Extending functionality
136
137Take a look at the sample app for a simple implementation showing the view in a `ViewPager` with an `OnClickListener` and an `OnLongClickListener`. I'll add more examples to this soon.