Android Glide in RecyclerView,only load visible item when page return,Kotlin
base on this article:
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
}
}