Android Glide preload RecyclerView切入后台不可见再切换可见只加载当前视野可见区域item图片,Kotlin

Android Glide preload RecyclerView切入后台不可见再切换可见只加载当前视野可见区域item图片,Kotlin

XML 复制代码
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

build.gradle文件:

XML 复制代码
plugins {
    id("org.jetbrains.kotlin.kapt")
}


implementation("com.github.bumptech.glide:glide:4.16.0")
kapt("com.github.bumptech.glide:compiler:4.16.0")

如果手机图片很多,假设已经将全部图片装入宫格的列表,在快速上下滑动过程中,由于glide会累积每一个图片的加载任务,如果图片比较大,上下滑动时间又很长,那么累积任务会很严重,导致异常发生,实现在RecyclerView切入后台(或不可见)时候,然后再次切换回来,只加载当前RecyclerView可见区域的item,非可见区域的图片加载任务,全部清除掉。

注意这里是利用onRestart生命周期模拟当RecyclerView不可见又切换回来的场景,这里可以换成其他场景,只要捕捉到RecyclerView已经消失在视野可见区域,比如利用fragment或者activity的可见性判断等等。

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 androidx.recyclerview.widget.RecyclerView.OnScrollListener
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import java.lang.ref.WeakReference

const val PHOTO_SIZE = 150

const val EMPTY = 0
const val LOAD = 1

class MainActivity : AppCompatActivity() {
    private val TAG = "Glide FLY"

    private var mItems = ArrayList<MyData>()
    private var mLayoutManager: GridLayoutManager? = null
    private var mAdapter: MyAdapter? = null
    private var mVisibleItemPosition: IntArray? = null

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

        init()

        mItems = readAllImage(applicationContext)
        mAdapter?.onChange(mItems)
    }

    override fun onResume() {
        super.onResume()
        Log.d(TAG, "onResume")
    }

    //当按了home键后,再次调出app,会进入onRestart
    override fun onRestart() {
        super.onRestart()
        Log.d(TAG, "onRestart")

        loadOnlyVisibleItem()
    }

    private fun loadOnlyVisibleItem() {
        Log.d(TAG, "可见区域 ${mVisibleItemPosition!![0]}->${mVisibleItemPosition!![1]}")
        for (i in mVisibleItemPosition!![0]..mVisibleItemPosition!![1]) {
            Log.d(TAG, "可见区域 ${mItems[i].pos} -- ${mItems[i].path}")
        }

        for (i in 0 until mItems.size) {
            if (i !in mVisibleItemPosition!![0]..mVisibleItemPosition!![1]) {
                if (mItems[i].status == LOAD) {
                    mItems[i].targetRef?.get()?.let {
                        GlideApp.with(this).clear(it)
                    }
                    mItems[i].status = EMPTY
                }
            }
        }
    }

    private fun getVisibleItemPosition(): IntArray {
        val first = mLayoutManager?.findFirstVisibleItemPosition()
        val last = mLayoutManager?.findLastVisibleItemPosition()
        return intArrayOf(first!!, last!!)
    }

    private fun init() {
        val spanCount = 8

        val rv = findViewById<RecyclerView>(R.id.recycler_view)

        mLayoutManager = GridLayoutManager(this, spanCount)
        rv.layoutManager = mLayoutManager

        rv.addOnScrollListener(object : OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                mVisibleItemPosition = getVisibleItemPosition()
            }
        })

        mAdapter = MyAdapter(this)
        rv.adapter = mAdapter
    }

    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
        )

        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))
        }

        cursor.close()

        return photos
    }
}

class MyAdapter(private val context: Context) : RecyclerView.Adapter<MyVH>() {
    private var items = ArrayList<MyData>()

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

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

    override fun onBindViewHolder(holder: MyVH, position: Int) {
        load(holder, position)
        holder.text.text = "$position"
    }

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

    private fun load(holder: MyVH, position: Int) {
        items[position].pos = position
        items[position].status = LOAD

        val target: Target<*> = GlideApp.with(holder.itemView.context)
            .asBitmap()
            .load(items[position].path)
            .centerCrop()
            .addListener(object : RequestListener<Bitmap> {
                override fun onLoadFailed(
                    e: GlideException?,
                    model: Any?,
                    target: Target<Bitmap>,
                    isFirstResource: Boolean
                ): Boolean {
                    items[position].status = EMPTY
                    return false
                }

                override fun onResourceReady(
                    resource: Bitmap,
                    model: Any,
                    target: Target<Bitmap>?,
                    dataSource: DataSource,
                    isFirstResource: Boolean
                ): Boolean {
                    holder.image.setImageBitmap(resource)
                    items[position].status = EMPTY

                    // 特别注意此处返回是true,而不能是false。
                    // 因为如果返回false,当按home键把app切入后台后,再按app图标调出app切换到前台可见,反复切换,
                    // 会造成Bitmap未回收的崩溃。
                    return true
                }
            }).preload(
                PHOTO_SIZE,
                PHOTO_SIZE
            )

        items[position].targetRef = WeakReference(target)
    }
}

class MyVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
    var image: AppCompatImageView
    var text: TextView

    init {
        image = itemView.findViewById(R.id.image)
        text = itemView.findViewById(R.id.text)
    }
}

class MyData(var path: String) {
    var targetRef: WeakReference<Target<*>>? = null
    var status = EMPTY
    var pos = 0
}
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" />

</RelativeLayout>
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:background="@android:color/holo_green_light"
        android:paddingLeft="1dp"
        android:paddingRight="1dp"
        android:text="-.-"
        android:textColor="@android:color/holo_red_dark"
        android:textSize="5dp" />
</RelativeLayout>
Kotlin 复制代码
import android.content.Context
import android.util.Log
import com.bumptech.glide.GlideBuilder
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.module.AppGlideModule

@GlideModule
class MyModule : AppGlideModule() {
    override fun applyOptions(context: Context, builder: GlideBuilder) {
        builder.setLogLevel(Log.DEBUG)
    }

    override fun isManifestParsingEnabled(): Boolean {
        return false
    }
}

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

Android Glide自定义AppGlideModule,让Glide在app启动后基于定制化GlideModule加载,kotlin_zhangphil的博客-CSDN博客在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。《Android图片加载与缓存开源框架:Android Glide》Android Glide是一个开源的图片加载和缓存处理的第三方框架。https://blog.csdn.net/zhangphil/article/details/131592226

相关推荐
inmK12 小时前
蓝奏云官方版不好用?蓝云最后一版实测:轻量化 + 不限速(避更新坑) 蓝云、蓝奏云第三方安卓版、蓝云最后一版、蓝奏云无广告管理工具、安卓网盘轻量化 APP
android·工具·网盘工具
giaoho2 小时前
Android 热点开发的相关api总结
android
咖啡の猫3 小时前
Android开发-常用布局
android·gitee
程序员老刘4 小时前
Google突然“变脸“,2026年要给全球开发者上“紧箍咒“?
android·flutter·客户端
Tans54 小时前
Androidx Lifecycle 源码阅读笔记
android·android jetpack·源码阅读
雨白4 小时前
实现双向滑动的 ScalableImageView(下)
android
峥嵘life4 小时前
Android Studio新版本编译release版本apk实现
android·ide·android studio
studyForMokey7 小时前
【Android 消息机制】Handler
android
敲代码的鱼哇7 小时前
跳转原生系统设置插件 支持安卓/iOS/鸿蒙UTS组件
android·ios·harmonyos
翻滚丷大头鱼7 小时前
android View详解—动画
android