Glide should be packaged in Kotlin like this (1)

Glide should be packaged in Kotlin like this (1)

Kotlin should write series like this:

SharedPreferences should be written like this in Kotlin

Glide should be packaged in Kotlin like this (2)

Preface

Glide is an image loading library officially recommended by Google, but everyone will encapsulate Glide for later maintenance of the project. The most common is simple encapsulation through the strategy mode, which can well support switching Glide, Picasso, etc. Let us review the implementation of the strategy mode, analyze its pros and cons, and then build a Glide package based on the advantages of Coil.

Discussion on Strategy Model

  • Write an interface, unify the method name, or have multiple methods, you can add them according to your needs

    /** Image loading strategy interface*/ public interface BaseImageLoaderStrategy { void loadImage (Context context,ImageOptions options) ; } Copy code
  • Implement specific loading strategies

    /*** Specific loading strategy, Glide loading framework*/ public class GlideImageLoader implements BaseImageLoaderStrategy { @Override public void loadImage (Context context, ImageOptions options) { Glide.with(context) .load(options.getUrl()) .placeholder(options.getPlaceHolder()) .into(options.getImgView()); } } Copy code
  • ImageOptions Build mode package

    /** * Set specific parameters, the code is tidy when there are too many parameters */ public class ImageOptions { private String url; //Network url private int placeHolder; //The picture displayed when it is not successfully loaded private ImageView imgView; //ImageView instance private ImageOptions (Builder builder) { this .url = builder.url; this .placeHolder = builder.placeHolder; this .imgView = builder.imgView; } public String getUrl () { return url; } public int getPlaceHolder () { return placeHolder; } public ImageView getImgView () { return imgView; } public static class Builder { private String url; private int placeHolder; private ImageView imgView; public Builder () { this .url = "" ; this .placeHolder = R.mipmap.ic_launcher; this .imgView = null ; } public Builder url (String url) { this .url = url; return this ; } public Builder placeHolder ( int placeHolder) { this .placeHolder = placeHolder; return this ; } public Builder imgView (ImageView imgView) { this .imgView = imgView; return this ; } public ImageOptions bulid () { return new new ImageOptions ( the this ); } } } Copy code
  • Scheduling strategy

    ** * Image loading frame strategy mode * Designed as a singleton mode, and exposed a method, you can set the loading mode, which image loading frame to use. */ public class ImageLoadreUtils { private static ImageLoadreUtils mInstance; private BaseImageLoaderStrategy imageLoaderStrategy; private ImageLoadreUtils () { //Use Glide loading mode by default imageLoaderStrategy = new GlideImageLoader(); } public static ImageLoadreUtils getInstance () { if (mInstance == null ) { synchronized (ImageLoadreUtils.class) { if (mInstance == null ) { mInstance = new ImageLoadreUtils(); return mInstance; } } } return mInstance; } /*** Set the image loading framework used*/ public void setImageLoaderStrategy (BaseImageLoaderStrategy imageLoaderStrategy) { this .imageLoaderStrategy = imageLoaderStrategy; } /*** Load image*/ public void loadImage (Context context, ImageLoader imageLoader) { imageLoaderStrategy.loadImage(context,imageLoader); } Copy code
  • Analysis of advantages and disadvantages

    Advantages: 1. Different picture frame loading strategies can be replaced according to needs; 2. Glide's same API call, which can be quickly changed when there are changes in demand; 3. Improve code reading and scalability

    Disadvantages: The strategy interface method needs to be changed or added according to requirements, resulting in the need for continuous maintenance and updating of strategy implementation

Coil is so beautiful

With the emergence of kotlin, due to the special fragrance of its extension function, a new picture frame coil appears, let s take a look at her use

//URL imageView.load( "https://www.example.com/image.jpg" ) //Multi-parameter imageView.load( "https://www.example.com/image.jpg" ) { crossfade( true ) placeholder(R.drawable.image) transformations(CircleCropTransformation()) } Copy code

The concise API usage is impressive. Is there any way to make Glide behave like a coil?

Glide package

  • inherit

    AppGlideModule
    Add OkHttp progress listener management, etc.

    //ProgressManager is responsible for the specific implementation of network picture progress management. Baidu @GlideModule(glideName = "IGlideModule" ) class GlideModule : AppGlideModule () { override fun applyOptions (context: Context , builder: GlideBuilder ) { } override fun registerComponents (context: Context , glide: Glide , registry: Registry ) { registry.replace(GlideUrl:: class .java, InputStream:: class .java, OkHttpUrlLoader.Factory(ProgressManager.okHttpClient)) } override fun isManifestParsingEnabled () : Boolean { return false } } Copy code
  • Encapsulate Glide

    /**Glide package*/ object GlideImageLoader { @JvmStatic fun loadImage (options: ImageOptions ) { Preconditions.checkNotNull(options, "ImageConfigImpl is required" ) val context = options.context Preconditions.checkNotNull(context, "Context is required" ) Preconditions.checkNotNull(options.imageView, "ImageView is required" ) val requestsWith = glideRequests(context) //Acquire according to the type val glideRequest = when (options.res) { is String -> requestsWith.load(options.res as String) is Bitmap -> requestsWith.load(options.res as Bitmap) is Drawable -> requestsWith.load(options.res as Drawable) is Uri -> requestsWith.load(options.res as Uri) isURL -> requestsWith.load(options.res as URL) is File -> requestsWith.load(options.res as File) is Int -> requestsWith.load(options.res as Int ) is ByteArray -> requestsWith.load(options .res as ByteArray) else -> requestsWith.load(options.res) } glideRequest.apply { //placeholder image, error image ... //Cache configuration, priority, thumbnail request ... //Animation, transformation into(GlideImageViewTarget(options.imageView, options.res)) } options.onProgressListener?.let { ProgressManager.addListener(options.res.toString(), options.onProgressListener) } } private fun glideRequests (context: Any ?) : GlideRequests { return when (context) { is Context -> IGlideModule.with(context) is Activity -> IGlideModule.with(context) is FragmentActivity -> IGlideModule.with(context) is Fragment -> IGlideModule.with(context) is android.app.Fragment -> IGlideModule.with(context) is View -> IGlideModule.with(context) else -> throw NullPointerException( "not support" ) } } /*** Clear local cache*/ suspend fun clearDiskCache (context: Context ) = withContext(Dispatchers.IO) { Glide. get (context).clearDiskCache() } /*** Clear memory cache*/ @JvmStatic fun clearMemory (context: Context ) { Glide. get (context).clearMemory(); } /*** Cancel image loading*/ @JvmStatic fun clearImage (context: Context , imageView: ImageView ?) { Glide. get (context).requestManagerRetriever[context].clear(imageView!!) } /*** Preload*/ @JvmStatic fun preloadImage (context: Any ?, url: String ?) { glideRequests(context).load(url).preload() } /*** Pause loading*/ @JvmStatic fun pauseRequests (context: Any ?) { glideRequests(context).pauseRequests() } /**Download*/ suspend fun downloadImage (context: Context , imgUrl: String ?) : File? = withContext(Dispatchers.IO) { ... } } Copy code
    /** * The configuration of the image loading library, encapsulating the original loading configuration properties, and converting */ class ImageOptions { /*** Load original resources */ var res: Any? = null /*** Show container*/ var imageView: ImageView? = null /*** The context in which the imageView exists or fragment\activity*/ var context: Any? = null get () { return field ?: imageView } /*** Load the placeholder image resource ID, if placeholder is 0, it means there is no placeholder image*/ @DrawableRes var placeHolderResId = 0 .... Omit its animation, error map and other attributes var centerCrop: Boolean = false /*** Network progress listener*/ var onProgressListener: OnProgressListener? = null /*** Load listener*/ var requestListener: OnImageListener? = null .... Omit the enumeration of caching strategy and priority etc. } Copy code

ImageView adds extension functions

  • Create a new ImageExt.kt, add a common loading method and a method with all configuration parameters

    private const val placeHolderImageView = 0 /** * Load local pictures */ @JvmOverloads fun ImageView. loadImage ( @RawRes @DrawableRes drawableId: Int , @RawRes @DrawableRes errorId: Int = drawableId) { this .loadImage(load = drawableId, placeHolderResId = drawableId, errorResId = errorId) } @JvmOverloads fun ImageView. loadImage (url: String ?, @RawRes @DrawableRes placeHolder: Int = placeHolderImageView, @RawRes @DrawableRes errorId: Int = placeHolder, loadListener: OnImageListener ? = Null ) { this .loadImage(load = url, placeHolderResId = placeHolder, errorResId = errorId, requestListener = loadListener) } @JvmOverloads fun ImageView. loadProgressImage (url: String ?, @RawRes @DrawableRes placeHolder: Int = placeHolderImageView, progressListener: OnProgressListener ? = Null ) { this .loadImage(load = url, placeHolderResId = placeHolder, errorResId = placeHolder, onProgressListener = progressListener) } @JvmOverloads fun ImageView. loadResizeImage (url: String ?, width: Int , height: Int , @RawRes @DrawableRes placeHolder: Int = placeHolderImageView) { this .loadImage(load = url, placeHolderResId = placeHolder, errorResId = placeHolder, size = ImageOptions.OverrideSize(width, height)) } @JvmOverloads fun ImageView. loadGrayImage (url: String ?, @DrawableRes placeHolder: Int = placeHolderImageView) { this .loadImage(load = url, placeHolderResId = placeHolder, errorResId = placeHolder, isGray = true ) } @JvmOverloads fun ImageView. loadBlurImage (url: String ?, radius: Int = 25 , sampling: Int = 4 , @DrawableRes placeHolder: Int = placeHolderImageView) { this.loadImage(load = url, placeHolderResId = placeHolder, errorResId = placeHolder, isBlur = true , blurRadius = radius, blurSampling = sampling) } @JvmOverloads fun ImageView. loadRoundCornerImage (url: String ?, radius: Int = 0 , type: ImageOptions . CornerType = ImageOptions.CornerType.ALL, @DrawableRes placeHolder: Int = placeHolderImageView) { this .loadImage(load = url, placeHolderResId = placeHolder , errorResId = placeHolder, isRoundedCorners = radius> 0 , roundRadius = radius, cornerType = type) } @JvmOverloads fun ImageView. loadCircleImage (url: String ?, borderWidth: Int = 0 , @ColorInt borderColor: Int = 0 , @DrawableRes placeHolder: Int = placeHolderImageView) { this.loadImage(load = url, placeHolderResId = placeHolder, errorResId = placeHolder , isCircle = true , borderWidth = borderWidth, borderColor = borderColor) } @JvmOverloads fun ImageView. loadBorderImage (url: String ?, borderWidth: Int = 0 , @ColorInt borderColor: Int = 0 , @DrawableRes placeHolder: Int = placeHolderImageView) { this .loadImage(load = url, placeHolderResId = placeHolder, errorResId = placeHolder , borderWidth = borderWidth, borderColor = borderColor) } @JvmOverloads fun ImageView. loadImage (load: Any ?, with: Any ? = This, //Placeholder image error image @DrawableRes placeHolderResId: Int = placeHolderImageView, placeHolderDrawable: Drawable ? = Null , @DrawableRes errorResId: Int = placeHolderImageView, errorDrawable : Drawable ? = Null , @DrawableRes fallbackResId: Int = placeHolderImageView, fallbackDrawable: Drawable ? = Null , //Cache strategy and other skipMemoryCache: Boolean = false , diskCacheStrategy: ImageOptions . DiskCache = ImageOptions.DiskCache.AUTOMATIC, //Priority priority: ImageOptions . LoadPriority = ImageOptions.LoadPriority.NORMAL, //thumbnail thumbnail: Float = 0 f, thumbnailUrl: Any ? = null , size: ImageOptions . OverrideSize ? = null , //gif or animation isAnim: Boolean = true , isCrossFade: Boolean = false , isCircle: Boolean = false , isGray: Boolean = false , isFitCenter: Boolean = false , centerCrop: Boolean = false , //output image pixel format format: Bitmap . Config ? = null , //a set of borders together borderWidth: Int = 0 , borderColor: Int = 0 , //A group of blurring is used together isBlur: Boolean = false , blurRadius: Int = 25 , blurSampling: Int = 4 , //rounded corners are used together isRoundedCorners: Boolean = false , roundRadius: Int = 0 , cornerType: ImageOptions . CornerType = ImageOptions.CornerType.ALL, //Custom converter vararg transformation: Transformation < Bitmap >, //Progress monitoring, request callback monitoring onProgressListener: OnProgressListener ? = null , requestListener: OnImageListener ? = null ) { GlideImageLoader.loadImage(ImageOptions().also { it.res = load it.imageView = this it.context = with it.placeHolderResId = placeHolderResId it.placeHolderDrawable = placeHolderDrawable it.errorResId = errorResId it.errorDrawable = errorDrawable it.fallbackResId = fallbackResId it.fallbackDrawable = fallbackDrawable it.isCrossFade = isCrossFade it.skipMemoryCache = skipMemoryCache it.isAnim = isAnim it.diskCacheStrategy = diskCacheStrategy it.priority = priority it.thumbnail = thumbnail it.thumbnailUrl = thumbnailUrl it.size = size it.isCircle = isCircle it.isGray = isGray it.centerCrop = centerCrop it.isFitCenter = isFitCenter it.format = format it.borderWidth = borderWidth it.borderColor = borderColor it.isBlur = isBlur it.blurRadius = blurRadius it.blurSampling = blurSampling it.isRoundedCorners = isRoundedCorners it.roundRadius = roundRadius it.cornerType = cornerType it.transformation = transformation it.onProgressListener = onProgressListener it.requestListener = requestListener }) } Copy code
  • Calling effect, due to different needs to use Glide's api combination, the extension function provides the one with the most parameters

    loadImage
    Function, call this function with optional parameters. Examples are as follows:

    //url imageView.loadImage(url, placeHolder = R.color.blue) //Special effects iv_3.loadBlurImage(url4) iv_4.loadCircleImage(url1) iv_5.loadBorderImage(url1, borderWidth = 10 , borderColor = Color.RED) iv_6.loadGrayImage(url1) iv_7.loadRoundCornerImage(url1, radius = 10 , type = ImageOptions.CornerType.ALL) iv_8.loadResizeImage(url2, width = 400 , height = 800 ) //Progress bar iv_0.loadProgressImage(url1, progressListener = object : OnProgressListener { override fun onProgress (isComplete: Boolean , percentage: Int , bytesRead: Long , totalBytes: Long ) { //Track progress Log.d( "TAG" , "onProgress: $percentage " ) } }) //Success and failure callback iv_2.loadImage(url4, loadListener = object : OnImageListener { override fun onSuccess (drawable: Drawable ?) { Toast.makeText(application, R.string.load_success, Toast.LENGTH_LONG).show() } override fun onFail (msg: String ?) { Toast.makeText(application, R.string.load_failed, Toast.LENGTH_LONG).show() } }) //Other special needs use optional parameters, loadImage(xxx = Xxxx,yyy = Yyy) use iv_9.loadImage(load = R.drawable.test, with = MainActivity@ this , placeHolderResId = R.color.black, requestListener = object : OnImageListener { override fun onSuccess (drawable: Drawable ?) { } override fun onFail (msg: String ?) { } }, onProgressListener = object : OnProgressListener { override fun onProgress (isComplete: Boolean , percentage: Int , bytesRead: Long , totalBytes: Long ) { } }, * = ArrayOf Transformation (GrayscaleTransformation (), CircleWithBorderTransformation (borderWidth = 0 , the borderColor = 0 ))) copying the code

summary

  • Finally, if you want to easily use it in the project, then package and release the jitpack warehouse, the project's open source address and documentation

    forJrking/ImageExt: An extension function set for loading image resources based on Glide encapsulated ImageView (github.com)

  • Since okhttp-based progress management is used, Glide's @GlideModule configuration method is used, which may conflict with your project's custom configuration. Currently, you can only pull the code and modify it yourself, and then rely on the Module method. If there is a better way to contact me to improve.

  • There are only a few common Android image loading libraries, and other libraries can be implemented by reference. Kotlin smells so good! ! !