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

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

 

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    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
    }
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 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>
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 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

猜你喜欢

转载自blog.csdn.net/zhangphil/article/details/132000010
今日推荐