Android Glide in RecyclerView,only load visible item when page return,Kotlin

Android Glide in RecyclerView,only load visible item when page return,Kotlin

base on this article:

Android Glide preload RecyclerView切入后台不可见再切换可见只加载当前视野可见区域item图片,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/132592405

XML 复制代码
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
XML 复制代码
plugins {
    id("org.jetbrains.kotlin.kapt")
}
XML 复制代码
    implementation("com.github.bumptech.glide:glide:4.16.0")
    kapt("com.github.bumptech.glide:compiler:4.16.0")
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.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target

const val PHOTO_SIZE = 150

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

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

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.recycler_view)
        init()
    }

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

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

    private fun init() {
        val spanCount = 8

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

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

        mAdapter = MyAdapter(this)
        rv.adapter = mAdapter

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

        mGlideLoad = GlideLoad(this, rv)
    }

    fun loadItem(holder: MyVH, position: Int) {
        val target: Target<*> = GlideApp.with(holder.itemView.context)
            .asBitmap()
            .load(mItems[position].path)
            .centerCrop()
            .addListener(object : RequestListener<Bitmap> {
                override fun onLoadFailed(
                    e: GlideException?,
                    model: Any?,
                    target: Target<Bitmap>,
                    isFirstResource: Boolean
                ): Boolean {
                    mGlideLoad?.setLoadStatus(position, GlideLoad.NONE, null)
                    return false
                }

                override fun onResourceReady(
                    resource: Bitmap,
                    model: Any,
                    target: Target<Bitmap>?,
                    dataSource: DataSource,
                    isFirstResource: Boolean
                ): Boolean {
                    holder.image.setImageBitmap(resource)
                    mGlideLoad?.setLoadStatus(position, GlideLoad.NONE, null)

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

        mGlideLoad?.setLoadStatus(position, GlideLoad.LOAD, target)
    }

    inner 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) {
            loadItem(holder, position)
            holder.text.text = "$position"
        }

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

    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 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)
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 androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.request.target.Target
import java.lang.ref.WeakReference

class GlideLoad {
    private val TAG = "Glide Load"

    companion object {
        const val NONE: Int = 0
        const val LOAD: Int = 1
    }

    private var mLayoutManager: GridLayoutManager? = null
    private var mRecyclerView: RecyclerView? = null
    private var mVisibleItemPosition: IntArray? = null
    private var mContext: Context? = null

    private var mStatusItems: ArrayList<Status>? = ArrayList()

    constructor(ctx: Context?, rv: RecyclerView?) {
        mContext = ctx
        mRecyclerView = rv

        repeat(mRecyclerView?.adapter?.itemCount!!) {
            mStatusItems?.add(Status())
        }

        mLayoutManager = mRecyclerView?.layoutManager as? GridLayoutManager
        mRecyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                mVisibleItemPosition = getVisibleItemPosition()
            }
        })
    }

    fun setLoadStatus(pos: Int, status: Int, target: Target<*>?) {
        mStatusItems!![pos].position = pos
        mStatusItems!![pos].status = status
        if (target != null) {
            mStatusItems!![pos].targetRef = WeakReference(target)
        } else {
            mStatusItems!![pos].targetRef = null
        }
    }

    fun clear() {
        Log.d(TAG, "可见区域 ${mVisibleItemPosition!![0]}->${mVisibleItemPosition!![1]}")

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

                    mStatusItems!![i].status = NONE
                    mStatusItems!![i].targetRef = null
                }
            }
        }
    }

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

    class Status {
        var targetRef: WeakReference<Target<*>>? = null
        var status = NONE
        var position = 0
    }
}
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
    }
}
相关推荐
赏金术士8 小时前
Kotlin 从入门到进阶 之作用域函数 & 优雅写法(五)
android·开发语言·kotlin
Ehtan_Zheng10 小时前
Android Compose 动画实践:内容切换与页面转场
android
Crystal32810 小时前
【终极指南】前端方面解决 uni-app APP 端 SSE 流式请求被缓冲拦截、无法实时渲染的问题
android·前端·ai编程
陆业聪11 小时前
技术选型决策树:什么团队、什么项目该选什么框架 | 跨平台框架深度对决(4)
android·架构设计
赏金术士12 小时前
Kotlin 从入门到进阶 之协程 Flow 模块(九)
开发语言·kotlin·php
JohnnyDeng9412 小时前
Kotlin 协程原理与 Android 中的最佳实践
android·kotlin·协程
Aleyn13 小时前
用 KSP 给 Navigation 3 加一层「跨模块路由」:nav3-helper 设计与使用
android·android jetpack·composer
GeekBug13 小时前
Claude Code 如何帮我写 80% 的 Android 样板代码
android·claude
dora13 小时前
手把手带你实现一个Android抽卡集图鉴功能
android
海雅达手持终端PDA13 小时前
海雅达Model 10X—高通6490工业三防平板,生产制造仓储管理应用
android·物联网·能源·制造·信息与通信·交通物流·平板