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