Android RecyclerView性能优化及Glide流畅加载图片丢帧率低的一种8宫格实现,Kotlin

211 篇文章 3 订阅
34 篇文章 0 订阅

Android RecyclerView性能优化及Glide流畅加载图片丢帧率低的一种8宫格实现,Kotlin

 

 

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

 

plugins {
    id 'org.jetbrains.kotlin.kapt'
}


    implementation 'com.github.bumptech.glide:glide:4.16.0'
    kapt 'com.github.bumptech.glide:compiler:4.16.0'

 

 

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.cache.InternalCacheDiskCacheFactory
import com.bumptech.glide.load.engine.cache.MemorySizeCalculator
import com.bumptech.glide.load.engine.executor.GlideExecutor
import com.bumptech.glide.module.AppGlideModule


@GlideModule
class MyGlideModule : AppGlideModule() {

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        super.applyOptions(context, builder)
        builder.setLogLevel(Log.DEBUG)

        val memoryCacheScreens = 200F
        val maxSizeMultiplier = 0.8F

        val calculator = MemorySizeCalculator.Builder(context)
            .setMemoryCacheScreens(memoryCacheScreens)
            .setBitmapPoolScreens(memoryCacheScreens)
            .setMaxSizeMultiplier(maxSizeMultiplier)
            .setLowMemoryMaxSizeMultiplier(maxSizeMultiplier * 0.8F)
            .setArrayPoolSize((1024 * 1024 * memoryCacheScreens).toInt())
            .build()

        builder.setMemorySizeCalculator(calculator)

        val diskCacheSize = 1024 * 1024 * 2000L
        builder.setDiskCache(InternalCacheDiskCacheFactory(context, diskCacheSize))


        val mSourceExecutor = GlideExecutor.newSourceBuilder()
            .setUncaughtThrowableStrategy(GlideExecutor.UncaughtThrowableStrategy.LOG)
            .setThreadCount(4)
            //.setThreadTimeoutMillis(1000) //线程读写超时时间。
            .setName("fly-SourceExecutor")
            .build()

        val mDiskCacheBuilder = GlideExecutor.newDiskCacheBuilder()
            .setThreadCount(1)
            //.setThreadTimeoutMillis(1000) //线程读写超时时间。
            .setName("fly-DiskCacheBuilder")
            .build()

        val mAnimationExecutor = GlideExecutor.newDiskCacheBuilder()
            .setThreadCount(1)
            //.setThreadTimeoutMillis(1000) //线程读写超时时间。
            .setName("fly-AnimationExecutor")
            .build()

        builder.setSourceExecutor(mSourceExecutor)
        builder.setDiskCacheExecutor(mDiskCacheBuilder)
        builder.setAnimationExecutor(mAnimationExecutor)
    }

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

 

 

import android.content.Context
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext


class MainActivity : AppCompatActivity() {
    companion object {
        const val TAG = "fly"

        const val VIEW_TYPE = 0

        const val PRELOAD_HEIGHT_COUNT = 2
        const val IMAGE_SIZE = 200
        const val ITEM_VIEW_CACHE_SIZE = 30
        const val SPAN_COUNT = 8
    }

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

        val rv = findViewById<RecyclerView>(R.id.rv)
        val layoutManager = MyLayoutManager(this, SPAN_COUNT)
        layoutManager.orientation = LinearLayoutManager.VERTICAL
        rv.layoutManager = layoutManager

        val adapter = MyAdapter(this)
        rv.adapter = adapter

        rv.setHasFixedSize(true)
        rv.setItemViewCacheSize(ITEM_VIEW_CACHE_SIZE)

        lifecycleScope.launch(Dispatchers.IO) {
            val items = readAllImage(this@MainActivity)
            withContext(Dispatchers.Main) {
                adapter.dataChanged(items)
            }
        }

        val ctx = this
        rv.setRecyclerListener(object : RecyclerView.RecyclerListener {
            override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
                val h: MyVH? = holder as? MyVH
                if (h != null) {
                    GlideApp.with(ctx).clear(h.image!!)
                }

                Log.d(TAG, "${h?.adapterPosition} onViewRecycled")
            }
        })
    }

    class MyAdapter : RecyclerView.Adapter<MyVH> {
        private var items = arrayListOf<MyData>()
        private var mContext: Context? = null

        constructor(ctx: Context) {
            this.mContext = ctx
        }

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

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyVH {
            val view = MyImageView(mContext!!)
            return MyVH(view)
        }

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

        override fun getItemViewType(position: Int): Int {
            return VIEW_TYPE
        }

        override fun onBindViewHolder(holder: MyVH, position: Int) {
            Log.d(TAG, "onBindViewHolder $position")

            val uri = items[holder.adapterPosition].path

            GlideApp.with(mContext!!)
                .asBitmap()
                .load(uri)
                .centerCrop()
                .override(IMAGE_SIZE, IMAGE_SIZE)
                .into(holder.image!!)
        }
    }

    class MyVH : RecyclerView.ViewHolder {
        var image: MyImageView? = null

        constructor(itemView: View) : super(itemView) {
            //image.layoutParams.height= IMAGE_SIZE
            //image.layoutParams.width= IMAGE_SIZE
            image = itemView as MyImageView
        }
    }

    class MyImageView : AppCompatImageView {
        constructor(ctx: Context) : super(ctx) {

        }
    }

    class MyLayoutManager : GridLayoutManager {
        constructor(ctx: Context, spanCount: Int) : super(ctx, spanCount) {

        }

        override fun getExtraLayoutSpace(state: RecyclerView.State?): Int {
            return PRELOAD_HEIGHT_COUNT * IMAGE_SIZE
        }
    }


    class MyData(var path: String, var index: Int)

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

避免使用xml定义ImageView装配到ViewHolder,这种情况在8宫格和更大宫格情况下,IO耗时,卡顿现象明显。

 

 

上面这种方式实现较为简洁、易懂,如果还要持续优化,想要更快,还有更复杂的方式:Android Glide自定义AppCompatImageView切分成若干小格子,每个小格子onDraw绘制Bitmap,Kotlin(1)_android appcompatimageview-CSDN博客 

 

 

 

 

Android性能优化RecyclerView预加载LayoutManager的getExtraLayoutSpace,Kotlin-CSDN博客文章浏览阅读104次。文章浏览阅读501次。文章浏览阅读428次。上面要预加载10条,每条item高度是100pix,也就是说,正确的情况下,如果RecyclerView不作任何调优,那它只加载当前屏幕可见区域position为0~21的item(每个item高度为100pix),如果配置了getExtraLayoutSpace,那么会多(Extra)加载10条position为22~31的item,其中22~31为屏幕底部不可见的区域中内容,被“预加载”出来。文章浏览阅读410次。https://zhangphil.blog.csdn.net/article/details/137589193

Android Glide load grid RecyclerView scroll smooth, high performance and ,Kotlin-CSDN博客文章浏览阅读393次,点赞14次,收藏8次。【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。现在结合他人的代码加以修改,给出一个以原始图形中心为原点,修剪图片为头像的工具类,此类可以直接在布局文件中加载使用,比。文章浏览阅读670次。https://blog.csdn.net/zhangphil/article/details/137520793

Android Glide配置AppGlideModule定制化线程池,Kotlin(1)-CSDN博客文章浏览阅读1k次,点赞14次,收藏20次。在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。文章浏览阅读670次。假设实现一个简单的功能,对传入要加载的path路径增加一定的筛选、容错或“重定向”,需要自定义一个模型,基于这个模型,让Glide自动匹配模型展开加载。文章浏览阅读670次。https://blog.csdn.net/zhangphil/article/details/137356178

 

  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中使用 RecyclerView 加载图片时,可以使用 Glide 库进行图片加载,同时可以通过以下几种方式进行滑动时图片加载优化: 1. 设置缓存策略:通过设置 Glide 的缓存策略,可以减少图片加载时的网络请求,提高加载速度。可以使用如下代码设置缓存策略: ``` RequestOptions requestOptions = new RequestOptions() .diskCacheStrategy(DiskCacheStrategy.ALL) .skipMemoryCache(false); Glide.with(context) .setDefaultRequestOptions(requestOptions) .load(url) .into(imageView); ``` 上述代码中,`diskCacheStrategy(DiskCacheStrategy.ALL)` 表示将图片缓存在磁盘中,`skipMemoryCache(false)` 表示不跳过内存缓存。 2. 设置占位符和错误图片:通过设置占位符和错误图片,可以在图片加载失败或者未加载完成时,显示一张默认的图片,避免出现空白的情况,同时也可以提高用户体验。可以使用如下代码设置占位符和错误图片: ``` RequestOptions requestOptions = new RequestOptions() .placeholder(R.drawable.placeholder) .error(R.drawable.error) .diskCacheStrategy(DiskCacheStrategy.ALL) .skipMemoryCache(false); Glide.with(context) .setDefaultRequestOptions(requestOptions) .load(url) .into(imageView); ``` 上述代码中,`placeholder(R.drawable.placeholder)` 表示在图片加载之前显示的占位符图片,`error(R.drawable.error)` 表示图片加载失败时显示的错误图片。 3. 取消加载任务:在 RecyclerView 滑动时,可能会存在用户滑动过快,导致部分图片加载完成,此时需要在滑动停止时,及时取消未完成的加载任务,避免加载任务过多,影响性能。可以使用如下代码取消加载任务: ``` @Override public void onViewDetachedFromWindow(@NonNull ViewHolder holder) { super.onViewDetachedFromWindow(holder); Glide.with(context).clear(holder.imageView); } ``` 上述代码中,`onViewDetachedFromWindow` 方法表示视图从窗口中移除时会被调用,通过 `Glide.with(context).clear(holder.imageView)` 取消当前视图中的加载任务。 通过上述优化方式,可以提高 RecyclerView 加载图片时的性能和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值