Android Glide预处理preload原始图片到成品resource & 预加载RecyclerViewPreloader,Kotlin

Android Glide预处理preload原始图片到成品resource & 预加载RecyclerViewPreloader,Kotlin

XML 复制代码
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
XML 复制代码
    implementation 'com.github.bumptech.glide:glide:4.15.1'
    kapt 'com.github.bumptech.glide:compiler:4.15.1'
    implementation ("com.github.bumptech.glide:recyclerview-integration:4.15.1") {
        // Excludes the support library because it's already included by Glide.
        transitive = false
    }
Kotlin 复制代码
import android.content.Context
import android.util.Log
import com.bumptech.glide.GlideBuilder
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool
import com.bumptech.glide.load.engine.cache.LruResourceCache
import com.bumptech.glide.module.AppGlideModule


@GlideModule
class MyModule : AppGlideModule() {
    companion object {
        val mLruResourceCache = LruResourceCache(1024 * 1024 * 9999)
        val mLruBitmapPool = LruBitmapPool(1024 * 1024 * 9999)

        fun debug(msg: String) {
            Log.d(
                TAG,
                "$msg"
            )
        }
    }

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        builder.setMemoryCache(mLruResourceCache)
        builder.setBitmapPool(mLruBitmapPool)

        builder.setLogLevel(Log.DEBUG)
        Log.d(TAG, "自定义配置")

        super.applyOptions(context, builder)
    }

    override fun isManifestParsingEnabled(): Boolean {
        return false
    }
}
XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity" />

</RelativeLayout>
Kotlin 复制代码
import android.content.Context
import android.graphics.Bitmap
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.ListPreloader
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.util.FixedPreloadSizeProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

const val PHOTO_SIZE = 80
const val TAG = "MyGlide"
const val preloadSize = 100

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        rvPreload()
    }

    private fun rvPreload() {
        val spanCount = 13

        var recyclerView: RecyclerView = findViewById(R.id.recycler_view)
        recyclerView?.layoutManager = GridLayoutManager(this, spanCount).apply {
            orientation = GridLayoutManager.VERTICAL
        }

        val adapter = MyAdapter(this)
        recyclerView?.adapter = adapter


        val heightCount = resources.displayMetrics.heightPixels / PHOTO_SIZE
        val max = 10 * heightCount * spanCount

        //如果没有句已加载且展示的格子再滑回去会出现残缺白块行。
        recyclerView.recycledViewPool.setMaxRecycledViews(0, max)

        CoroutineScope(Dispatchers.IO).launch {
            val items = readAllImage(applicationContext)

            //以len为长度值,将原始的列表切分为 [0-(len-1)],[len-列表结尾]两条。
            //第一条较短,快速完成原始图片预处理,紧急为用户快速展示图片(此时Glide将从resource直接装入内存)
            //第二条很长,适合在app冷启动后,后台静默加载。
            val len = 600
            val segment1 = items.subList(0, len)
            val segment2 = items.subList(len, items.size - 1)

            Log.d(TAG, "预处理 $len ...")
            segment1.forEachIndexed { index, myData ->
                glidePreload(myData)
            }
            Log.d(TAG, "预处理 $len")

            // 这段时间是拍脑瓜拍出来的。
            // Glide需要一定时间才能完成len个资源的预处理,把原始数据转换为resource成品。
            // 如果不等待不延迟,直接通知adapter更新数据,绝大多数情况下,Glide此时还没来得及完成预处理,没有预处理快速显示的效果。
            delay(10_000L)

            withContext(Dispatchers.Main) {
                Log.d(TAG, "预处理 $len done")
                adapter.onChange(items)
            }

            Log.d(TAG, "预处理 第二批 ...")
            segment2.forEachIndexed { index, myData ->
                glidePreload(myData)
            }
        }

        val preloadSizeProvider = FixedPreloadSizeProvider<MyData>(
            PHOTO_SIZE,
            PHOTO_SIZE
        )

        val preloadModelProvider = MyPreloadModelProvider(this, adapter)
        val preloader: RecyclerViewPreloader<MyData> = RecyclerViewPreloader(
            GlideApp.with(this),
            preloadModelProvider,
            preloadSizeProvider,
            preloadSize
        )

        recyclerView?.addOnScrollListener(preloader)
    }

    private fun glidePreload(myData: MyData) {
        //本身是线程化的,很快返回,不阻塞主线程。Glide将在后台线程中decode原始图片,加工成resource成品。
        GlideApp.with(this)
            .asBitmap()
            .load(myData.path)
            .centerCrop()
            .override(PHOTO_SIZE)
            .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
            .preload(PHOTO_SIZE, PHOTO_SIZE)
    }


    class MyPreloadModelProvider(
        private val ctx: Context,
        private val adapter: MyAdapter,
    ) : ListPreloader.PreloadModelProvider<MyData> {
        override fun getPreloadItems(position: Int): MutableList<MyData> {
            return mutableListOf(adapter.getItems()[position])
        }

        override fun getPreloadRequestBuilder(item: MyData): GlideRequest<Bitmap>? {
            return GlideApp.with(ctx)
                .asBitmap()
                .load(item.path)
                .centerCrop()
                .override(PHOTO_SIZE)
        }
    }

    class MyAdapter(private val ctx: Context) :
        RecyclerView.Adapter<MyViewHolder>() {
        private var items: MutableList<MyData>? = null

        fun onChange(items: MutableList<MyData>) {
            this.items = items
            notifyDataSetChanged()
        }

        fun getItems(): MutableList<MyData> {
            return items!!
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
            val view = LayoutInflater.from(ctx).inflate(R.layout.item, parent, false)
            val params = view.layoutParams
            params.width = PHOTO_SIZE
            params.height = PHOTO_SIZE
            return MyViewHolder(view)
        }

        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            GlideApp.with(ctx)
                .asBitmap()
                .centerCrop()
                .override(PHOTO_SIZE, PHOTO_SIZE)
                .load(items?.get(position)?.path)
                .into(holder.image)

            holder.text.text = "$position"
        }

        override fun getItemCount(): Int {
            return items?.size ?: 0
        }

//        override fun getItemId(position: Int): Long {
//            return RecyclerView.NO_ID
//        }

        override fun getItemViewType(position: Int): Int {
            return 0
        }
    }

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val image: AppCompatImageView = itemView.findViewById(R.id.image)
        val text: TextView = itemView.findViewById(R.id.text)
    }

    private fun readAllImage(context: Context): ArrayList<MyData> {
        val photos = ArrayList<MyData>()

        //读取手机图片
        val cursor = context.contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null,
            null,
            null,
            null
        )
        var index = 0
        while (cursor!!.moveToNext()) {
            //图片路径 uri
            val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))

            //图片名称
            //val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))

            //图片大小
            //val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))

            photos.add(MyData(path, index++))
        }
        cursor.close()

        return photos
    }

    class MyData(var path: String, val index: Int)
}
XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="1px">

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_launcher_background" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="-.-"
        android:textColor="@android:color/holo_red_dark"
        android:textSize="8dp" />
</RelativeLayout>

Android GlideApp FixedPreloadSizeProvider RecyclerViewPreloader,mix Java&Kotlin_zhangphil的博客-CSDN博客【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。《Android图片加载与缓存开源框架:Android Glide》Android Glide是一个开源的图片加载和缓存处理的第三方框架。https://blog.csdn.net/zhangphil/article/details/131905329Android GlideApp GlideRequest FixedPreloadSizeProvider RecyclerViewPreloader,kotlin_zhangphil的博客-CSDN博客【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。《Android图片加载与缓存开源框架:Android Glide》Android Glide是一个开源的图片加载和缓存处理的第三方框架。https://blog.csdn.net/zhangphil/article/details/131813200

相关推荐
天花板之恋1 小时前
MutableStateFlow、StateFlow、LiveData在Compose中的运用
kotlin·workflow
我命由我123451 小时前
Android PDF 操作 - AndroidPdfViewer 显示 PDF 异常清单(数据为 null、数据为空、PDF 文件损坏、非 PDF 文件)
android·java·java-ee·pdf·android studio·android-studio·android runtime
zhilin_tang2 小时前
揭开Linux跨平台 adb调试原理神秘面纱
android·linux
撩得Android一次心动3 小时前
Android 四大组件——Activity
android
TimeFine3 小时前
Android 网络请求超时?可能与连接池和脏连接有关
android
Digitally5 小时前
如何将安卓手机备份到电脑?7种方法
android
火柴就是我6 小时前
android:enableJetifier=true 再学习
android·flutter
杨筱毅6 小时前
【Android】【底层原理】深入解析SELinux模块
android·底层机制
Tom4i6 小时前
基于 Launcher3 的 iOS 风格桌面 04 拖拽和移位
android
2501_915106326 小时前
iOS 反编译防护工具与实战组合 从静态侦察到 IPA 成品加固的工程化路径
android·ios·小程序·https·uni-app·iphone·webview