SubsamplingScaleImageView can be used in the large picture display, display images has the following characteristics
- When only part of the picture is displayed after magnification, use the official BitmapRegionDecoder class to decode part of the picture
- Select an appropriate sampling rate for sampling according to the current scaling ratio, and split the decoded image into multiple tiles according to the current displayable range and store them in tileMap, which stores one of the bitmaps after splitting
- When the scaling ratio changes, determine whether the tiles in the current Tilemap are still useful. If not, recycle the bitmap, re-parse the original image and split it into multiple tiles
use
It’s easy to use, in XML
<com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
android:width="match_parent"
android:height="match_parent"
android:id="@+id/imageView"/>
Copy the code
Set the image resource in Java code
// Use an image from assets
view.setImage(ImageSource.asset("sanmartino.jpg"));
// Use the image in the res directory
imageView.setImage(ImageSource.resource(R.drawable.monkey));
// Use an absolute path
imageView.setImage(ImageSource.uri("/sdcard/DCIM/DSCM00123.JPG"));
Copy the code
Image loading process
To see how it displays the image, view.setimage (ImageSource. Asset (“sanmartino.jpg”)); For example (others are similar)
Setting Basic Parameters
Look at the ImageSource. Asset (” sanmartino. JPG “)
public static ImageSource asset(@NonNull String assetName) {
//noinspection ConstantConditions
if (assetName == null) {
throw new NullPointerException("Asset name must not be null");
}
return uri(ASSET_SCHEME + assetName);
}
Copy the code
public static ImageSource uri(@NonNull String uri) {
//noinspection ConstantConditions
// omit extraneous code
return new ImageSource(Uri.parse(uri));
}
Copy the code
private ImageSource(@NonNull Uri uri) {
// #114 If file doesn't exist, attempt to url decode the URI and try again
String uriString = uri.toString();
// omit extraneous code
this.bitmap = null;
this.uri = uri;
this.resource = null;
this.tile = true;
}
Copy the code
You can see that an ImageSource is returned with the address of the image and tiles enabled by default (tileMap is used to split the image into tiles containing local bitmaps)
Now look at the method setImage
public final void setImage(@NonNull ImageSource imageSource) {
setImage(imageSource, null.null);
}
Copy the code
public final void setImage(@NonNull ImageSource imageSource, ImageSource previewSource, ImageViewState state) {
//noinspection ConstantConditions
if (imageSource == null) {
throw new NullPointerException("imageSource must not be null");
}
// This method mainly sets various parameters (zoom, display range, matrix, etc.) to initial (or default) values.
// We pass true to indicate that this is a new image, so we will recycle bitmap, TileMap, decoder, etc
reset(true);
if(state ! =null) { restoreState(state); }
if(previewSource ! =null) {
// omit the preview image
}
if(imageSource.getBitmap() ! =null&& imageSource.getSRegion() ! =null) {
// the bitmap is null
} else if(imageSource.getBitmap() ! =null) {
// the bitmap is null
} else {
// sRegion is null
sRegion = imageSource.getSRegion();
uri = imageSource.getUri();
if (uri == null&& imageSource.getResource() ! =null) {
// omit, where the URI is not null
}
if(imageSource.getTile() || sRegion ! =null) {
// Load the bitmap using tile decoding.
// omit, where sRegion is null
} else {
// Load the bitmap as a single image.
//bitmapDecoderFactory can be understood as a factory class for obtaining image decoders
BitmapLoadTask task = new BitmapLoadTask(this, getContext(), bitmapDecoderFactory, uri, false);
execute(task);
}
}
}
Copy the code
Bitmap loaded
Next up is BitmapLoadTask, a background task class that inherits from AsyncTask
Look at its doInBackground method
protected Integer doInBackground(Void... params) {
try {
String sourceUri = source.toString();
// This class uses virtual references to hold the context, decoderFactory, and View to prevent memory leaks
Context context = contextRef.get();
DecoderFactory<? extends ImageDecoder> decoderFactory = decoderFactoryRef.get();
SubsamplingScaleImageView view = viewRef.get();
if(context ! =null&& decoderFactory ! =null&& view ! =null) {
view.debug("BitmapLoadTask.doInBackground");
//decoderFactory.make() to get a decoder class
bitmap = decoderFactory.make().decode(context, source);
return view.getExifOrientation(context, sourceUri);
}
} catch (Exception e) {
Log.e(TAG, "Failed to load bitmap", e);
this.exception = e;
} catch (OutOfMemoryError e) {
Log.e(TAG, "Failed to load bitmap - OutOfMemoryError", e);
this.exception = new RuntimeException(e);
}
return null;
}
Copy the code
Decoderfactory.make () will get a decoder class SkiaImageDecoder, look at the SkiaImageDecoder#decode method
public Bitmap decode(Context context, @NonNull Uri uri) throws Exception {
String uriString = uri.toString();
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap;
options.inPreferredConfig = bitmapConfig;
if (uriString.startsWith(RESOURCE_PREFIX)) {
// If you are using an image in the res directory, omit it
} else if (uriString.startsWith(ASSET_PREFIX)) {
String assetName = uriString.substring(ASSET_PREFIX.length());
bitmap = BitmapFactory.decodeStream(context.getAssets().open(assetName), null, options);
} else if (uriString.startsWith(FILE_PREFIX)) {
// If you are using a picture in the phone directory, omit it
} else {
// In other cases, omit
}
if (bitmap == null) {
// exception handling, omitted
}
return bitmap;
}
Copy the code
Continue with the BitmapLoadTask#onPostExecute method
protected void onPostExecute(Integer orientation) {
SubsamplingScaleImageView subsamplingScaleImageView = viewRef.get();
if(subsamplingScaleImageView ! =null) {
if(bitmap ! =null&& orientation ! =null) {
if (preview) {
subsamplingScaleImageView.onPreviewLoaded(bitmap);
} else {
// The bitmap loaded successfully
subsamplingScaleImageView.onImageLoaded(bitmap, orientation, false);
}
} else if(exception ! =null&& subsamplingScaleImageView.onImageEventListener ! =null) {
// exception handling, omitted
}
}
}
Copy the code
Length limit, the next look here SubsamplingScaleImageView parsing (below)