Glide遇到DataBinding的花式用法

前言

主流的图片加载框架越来越多,对应的配置也很麻烦,加载一张图片需要配置一堆参数才能达到需求,使用 Glide + DataBinding + Kotlin扩展简单的几个类,可以完成方便快速的统一进行配置。


1.定义缓存配置项

class DiskCacheOptions private constructor(builder: Builder){

    val bitmapPoolSize: Float
    val memoryCacheSize: Float
    val diskCacheDirPath: String?
    val diskCacheFolderName: String
    val diskCacheSize: Long

    init {
        this.bitmapPoolSize = builder.bitmapPoolSize
        this.memoryCacheSize = builder.memoryCacheSize
        this.diskCacheDirPath = builder.diskCacheDirPath
        this.diskCacheFolderName = builder.diskCacheFolderName
        this.diskCacheSize = builder.diskCacheSize
    }

    data class Builder(
        var bitmapPoolSize: Float = .0f,
        var memoryCacheSize: Float = .0f,
        var diskCacheDirPath: String? = null,
        var diskCacheFolderName: String = "Image",
        var diskCacheSize: Long = 1 * 1024 * 1024
    ) {

        /**
         * Bitmap池size, Bitmap池,值范围 1-3,建议在 Application 中设置
         */
        fun setBitmapPoolSize(@FloatRange(from = 1.0, to = 3.0) bitmapPoolSize: Float) = apply { this.bitmapPoolSize = bitmapPoolSize }

        /**
         * 内存缓存size, 默认内存缓存, 值范围 1-2 建议在 Application 中设置
         */
        fun setMemoryCacheSize(@FloatRange(from = 1.0, to = 2.0) memoryCacheSize: Float) = apply { this.memoryCacheSize = memoryCacheSize }

        /**
         * 磁盘缓存文件夹地址
         */
        fun setDiskCacheDirPath(dirPath: String) = apply { this.diskCacheDirPath = dirPath }

        /**
         * 磁盘缓存文件夹目录,默认 image
         */
        fun setDiskCacheFolderName(folderName: String)  =apply { this.diskCacheFolderName = folderName }

        /**
         * 磁盘缓存size,默认 1G
         */
        fun setDiskCacheSize(size: Long) = apply { this.diskCacheSize = size }

        fun build() = DiskCacheOptions(this)
    }
}

2.编写一个ImageLoader单例类

ImageLoader类主要用来初始化缓存配置项的参数以及Glide的一些其它的函数封装。

Tip:切勿在代码里面直接使用Glide.xxx函数,使用ImageLoader类可以方便切换其他图片加载框架。

class ImageLoader {

    companion object {

        @Volatile
        private var INSTANCES: ImageLoader? = null

        fun getDefault(): ImageLoader = INSTANCES ?: synchronized(this){ ImageLoader().also { INSTANCES = it }}
    }

    fun diskCacheOptions() = DiskCacheOptions.Builder()

    /**
     * 暂停当前上下文中的Glide请求
     */
    fun pauseRequests(context: Context) {
        Glide.with(context).pauseRequests()
    }

    /**
     * 暂停所有Glide请求
     */
    fun pauseAllRequests(context: Context) {
        Glide.with(context).pauseAllRequests()
    }

    /**
     * 恢复Glide请求
     */
    fun resumeRequests(context: Context) {
        Handler(Looper.getMainLooper()).post { Glide.with(context).resumeRequests() }
    }

    /**
     * 清除Glide的磁盘缓存
     */
    fun clearDiskCache(context: Context) {
        Glide.get(context).clearDiskCache()
    }

    /**
     * 清除Glide的磁盘缓存,与上面函数作用一致。获取上下文的方式不同而已。
     */
    fun clear(view: View) {
        Glide.with(view).clear(view)
    }

    /**
     * 清除Glide的内存缓存
     */
    fun clearMemory(context: Context) {
        Glide.get(context).clearMemory()
    }

    /**
     * 清除一些内存缓存,具体数量取决于给应用分配的级别。
     */
    fun trimMemory(context: Context, level: Int) {
        Glide.get(context).trimMemory(level)
    }

    /**
     * 获取磁盘缓存的数据大小,单位:KB
     */
    fun getDiskCacheSize(context: Context): Long {
        val options = diskCacheOptions().build()
        val diskCacheDirPath = options.diskCacheDirPath ?: context.filesDir.path
        val diskCacheFolderName = options.diskCacheFolderName
        val file = File("$diskCacheDirPath${File.separator}$diskCacheFolderName")
        var blockSize = 0L
        try {
            blockSize = if (file.isDirectory) getFileSizes(file) else getFileSize(file)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return blockSize
    }

    private fun getFileSizes(file: File): Long {
        var size = 0L
        file.listFiles()?.forEach {
            if (it.isDirectory) {
                size += getFileSizes(it)
            } else {
                try {
                    size += getFileSize(it)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }
        return size
    }

    private fun getFileSize(file: File): Long {
        var size = 0L
        if (file.exists()) {
            FileInputStream(file).use {
                size = it.available().toLong()
            }
        }
        return size
    }
}

3.集成AppGlideModule类,配置内存与磁盘缓存设置

@GlideModule
class DiskCacheModule: AppGlideModule() {

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        // 设置内存缓存
        val mOptions = ImageLoader.getDefault().diskCacheOptions().build()
        // 内存缓存大小计算器
        val mCalculator = MemorySizeCalculator.Builder(context)
            .setBitmapPoolScreens(mOptions.bitmapPoolSize)
            .setMemoryCacheScreens(mOptions.memoryCacheSize)
            .build()
        // Bitmap池, LruBitmapPool:负责控制缓存
        builder.setBitmapPool(LruBitmapPool(mCalculator.bitmapPoolSize.toLong()))
        // 内存缓存
        builder.setMemoryCache(LruResourceCache(mCalculator.memoryCacheSize.toLong()))
        // 设置磁盘缓存
        val mDiskCacheDirPath = mOptions.diskCacheDirPath ?: context.filesDir.path
        builder.setDiskCache(DiskLruCacheFactory(mDiskCacheDirPath, mOptions.diskCacheFolderName, mOptions.diskCacheSize))
        // 日志
        builder.setLogLevel(Log.ERROR)
    }

    override fun isManifestParsingEnabled(): Boolean = false
}

4.编写Glide的扩展,添加DataBinding注解,方便在xml中调用。

@BindingAdapter(
    value = ["imageUrl", "placeholder", "error", "fallback", "loadWidth", "loadHeight", "cacheEnable"],
    requireAll = false
)
fun setImageUrl(
    view: ImageView,
    source: Any? = null,
    placeholder: Drawable? = null,
    error: Drawable? = null,
    fallback: Drawable? = null,
    width: Int? = -1,
    height: Int? = -1,
    cacheEnable: Boolean? = true
) {
	// 计算位图尺寸,如果位图尺寸固定,加载固定大小尺寸的图片,如果位图未设置尺寸,那就加载原图,Glide加载原图时,override参数设置 -1 即可。
    val widthSize = (if ((width ?: 0) > 0) width else view.width) ?: -1
    val heightSize = (if ((height ?: 0) > 0) height else view.height) ?: -1
    // 根据定义的 cacheEnable 参数来决定是否缓存
    val diskCacheStrategy = if (cacheEnable == true) DiskCacheStrategy.AUTOMATIC else DiskCacheStrategy.NONE
    // 设置编码格式,在Android 11(R)上面使用高清无损压缩格式 WEBP_LOSSLESS , Android 11 以下使用PNG格式,PNG格式时会忽略设置的 quality 参数。
    val encodeFormat = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) Bitmap.CompressFormat.WEBP_LOSSLESS else Bitmap.CompressFormat.PNG
    Glide.with(view.context)
        .asDrawable()
        .load(source)
        .placeholder(placeholder)
        .error(error)
        .fallback(fallback)
        .thumbnail(0.33f)
        .override(widthSize, heightSize)
        .skipMemoryCache(false)
        .sizeMultiplier(0.5f)
        .format(DecodeFormat.PREFER_ARGB_8888)
        .encodeFormat(encodeFormat)
        .encodeQuality(80)
        .diskCacheStrategy(diskCacheStrategy)
        .transition(DrawableTransitionOptions.withCrossFade())
        .into(view)
}

5.xml中的用法

app:imageUrl="@{data.contentUri}"
app:error="@{@drawable/draw_media_placeholder}"
app:fallback="@{@drawable/draw_media_placeholder}"
app:placeholder="@{@drawable/draw_media_placeholder}"

<androidx.appcompat.widget.AppCompatImageView
    android:id="@+id/media_thumbnail"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="@{(view) -> data.onClick(view, data)}"
    app:cacheEnable="@{false}"
    app:imageUrl="@{data.source.contentUri}"
    app:error="@{@drawable/draw_media_placeholder}"
    app:fallback="@{@drawable/draw_media_placeholder}"
    app:placeholder="@{@drawable/draw_media_placeholder}" />
上一篇:databinding原理,靠着这份面试题跟答案,完整版开放下载


下一篇:Jetpack使用(ViewModel、DataBinding、Binding)