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)