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

相关推荐
BU摆烂会噶1 分钟前
【LangGraph】House_Agent 实战(四):预定流程 —— 中断与人工干预
android·人工智能·python·langchain
AI玫瑰助手1 分钟前
Python运算符:比较运算符(等于不等等于大于小于)与返回值
android·开发语言·python
new_dev32 分钟前
Python实现Android自动化打包工具:加固、签名、多渠道一键完成
android·python·自动化
小孔龙32 分钟前
Android `<activity-alias>` 指南:动态图标 · 多入口 · 重命名兼容
android·程序员·掘金·日新计划
QING6181 小时前
Kotlin inline 实战详解 —— 新手须知
android·kotlin·android jetpack
ElevenS_it1881 小时前
MySQL慢查询监控与告警实战:从slow_log采集到分钟级定位慢SQL的完整链路配置
android·sql·mysql
沐言人生1 小时前
ReactNative 源码分析12——Native View创建流程onBatchComplete
android·react native
caicai_xiaobai1 小时前
QT搭建安卓开发环境
android
YF02111 小时前
Android 异形屏与横屏全屏沉浸式适配技术方案
android·app
Ehtan_Zheng1 小时前
Kotlin Flow:combine()、merge() 和 zip() 的区别 —— 不要再互相替代使用
kotlin