当前位置:   article > 正文

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

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

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

 

 

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

 

  1. plugins {
  2. id 'org.jetbrains.kotlin.kapt'
  3. }
  4. implementation 'com.github.bumptech.glide:glide:4.16.0'
  5. kapt 'com.github.bumptech.glide:compiler:4.16.0'

 

 

  1. import android.content.Context
  2. import android.util.Log
  3. import com.bumptech.glide.GlideBuilder
  4. import com.bumptech.glide.annotation.GlideModule
  5. import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
  6. import com.bumptech.glide.load.engine.cache.MemorySizeCalculator
  7. import com.bumptech.glide.load.engine.executor.GlideExecutor
  8. import com.bumptech.glide.module.AppGlideModule
  9. @GlideModule
  10. class MyGlideModule : AppGlideModule() {
  11. override fun applyOptions(context: Context, builder: GlideBuilder) {
  12. super.applyOptions(context, builder)
  13. builder.setLogLevel(Log.DEBUG)
  14. val memoryCacheScreens = 200F
  15. val maxSizeMultiplier = 0.8F
  16. val calculator = MemorySizeCalculator.Builder(context)
  17. .setMemoryCacheScreens(memoryCacheScreens)
  18. .setBitmapPoolScreens(memoryCacheScreens)
  19. .setMaxSizeMultiplier(maxSizeMultiplier)
  20. .setLowMemoryMaxSizeMultiplier(maxSizeMultiplier * 0.8F)
  21. .setArrayPoolSize((1024 * 1024 * memoryCacheScreens).toInt())
  22. .build()
  23. builder.setMemorySizeCalculator(calculator)
  24. val diskCacheSize = 1024 * 1024 * 2000L
  25. builder.setDiskCache(InternalCacheDiskCacheFactory(context, diskCacheSize))
  26. val mSourceExecutor = GlideExecutor.newSourceBuilder()
  27. .setUncaughtThrowableStrategy(GlideExecutor.UncaughtThrowableStrategy.LOG)
  28. .setThreadCount(4)
  29. //.setThreadTimeoutMillis(1000) //线程读写超时时间。
  30. .setName("fly-SourceExecutor")
  31. .build()
  32. val mDiskCacheBuilder = GlideExecutor.newDiskCacheBuilder()
  33. .setThreadCount(1)
  34. //.setThreadTimeoutMillis(1000) //线程读写超时时间。
  35. .setName("fly-DiskCacheBuilder")
  36. .build()
  37. val mAnimationExecutor = GlideExecutor.newDiskCacheBuilder()
  38. .setThreadCount(1)
  39. //.setThreadTimeoutMillis(1000) //线程读写超时时间。
  40. .setName("fly-AnimationExecutor")
  41. .build()
  42. builder.setSourceExecutor(mSourceExecutor)
  43. builder.setDiskCacheExecutor(mDiskCacheBuilder)
  44. builder.setAnimationExecutor(mAnimationExecutor)
  45. }
  46. override fun isManifestParsingEnabled(): Boolean {
  47. return false
  48. }
  49. }

 

 

  1. import android.content.Context
  2. import android.os.Bundle
  3. import android.provider.MediaStore
  4. import android.util.Log
  5. import android.view.View
  6. import android.view.ViewGroup
  7. import androidx.appcompat.app.AppCompatActivity
  8. import androidx.appcompat.widget.AppCompatImageView
  9. import androidx.lifecycle.lifecycleScope
  10. import androidx.recyclerview.widget.GridLayoutManager
  11. import androidx.recyclerview.widget.LinearLayoutManager
  12. import androidx.recyclerview.widget.RecyclerView
  13. import kotlinx.coroutines.Dispatchers
  14. import kotlinx.coroutines.launch
  15. import kotlinx.coroutines.withContext
  16. class MainActivity : AppCompatActivity() {
  17. companion object {
  18. const val TAG = "fly"
  19. const val VIEW_TYPE = 0
  20. const val PRELOAD_HEIGHT_COUNT = 2
  21. const val IMAGE_SIZE = 200
  22. const val ITEM_VIEW_CACHE_SIZE = 30
  23. const val SPAN_COUNT = 8
  24. }
  25. override fun onCreate(savedInstanceState: Bundle?) {
  26. super.onCreate(savedInstanceState)
  27. setContentView(R.layout.activity_main)
  28. val rv = findViewById<RecyclerView>(R.id.rv)
  29. val layoutManager = MyLayoutManager(this, SPAN_COUNT)
  30. layoutManager.orientation = LinearLayoutManager.VERTICAL
  31. rv.layoutManager = layoutManager
  32. val adapter = MyAdapter(this)
  33. rv.adapter = adapter
  34. rv.setHasFixedSize(true)
  35. rv.setItemViewCacheSize(ITEM_VIEW_CACHE_SIZE)
  36. lifecycleScope.launch(Dispatchers.IO) {
  37. val items = readAllImage(this@MainActivity)
  38. withContext(Dispatchers.Main) {
  39. adapter.dataChanged(items)
  40. }
  41. }
  42. val ctx = this
  43. rv.setRecyclerListener(object : RecyclerView.RecyclerListener {
  44. override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
  45. val h: MyVH? = holder as? MyVH
  46. if (h != null) {
  47. GlideApp.with(ctx).clear(h.image!!)
  48. }
  49. Log.d(TAG, "${h?.adapterPosition} onViewRecycled")
  50. }
  51. })
  52. }
  53. class MyAdapter : RecyclerView.Adapter<MyVH> {
  54. private var items = arrayListOf<MyData>()
  55. private var mContext: Context? = null
  56. constructor(ctx: Context) {
  57. this.mContext = ctx
  58. }
  59. fun dataChanged(items: ArrayList<MyData>) {
  60. this.items = items
  61. notifyDataSetChanged()
  62. }
  63. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyVH {
  64. val view = MyImageView(mContext!!)
  65. return MyVH(view)
  66. }
  67. override fun getItemCount(): Int {
  68. return items.size
  69. }
  70. override fun getItemViewType(position: Int): Int {
  71. return VIEW_TYPE
  72. }
  73. override fun onBindViewHolder(holder: MyVH, position: Int) {
  74. Log.d(TAG, "onBindViewHolder $position")
  75. val uri = items[holder.adapterPosition].path
  76. GlideApp.with(mContext!!)
  77. .asBitmap()
  78. .load(uri)
  79. .centerCrop()
  80. .override(IMAGE_SIZE, IMAGE_SIZE)
  81. .into(holder.image!!)
  82. }
  83. }
  84. class MyVH : RecyclerView.ViewHolder {
  85. var image: MyImageView? = null
  86. constructor(itemView: View) : super(itemView) {
  87. //image.layoutParams.height= IMAGE_SIZE
  88. //image.layoutParams.width= IMAGE_SIZE
  89. image = itemView as MyImageView
  90. }
  91. }
  92. class MyImageView : AppCompatImageView {
  93. constructor(ctx: Context) : super(ctx) {
  94. }
  95. }
  96. class MyLayoutManager : GridLayoutManager {
  97. constructor(ctx: Context, spanCount: Int) : super(ctx, spanCount) {
  98. }
  99. override fun getExtraLayoutSpace(state: RecyclerView.State?): Int {
  100. return PRELOAD_HEIGHT_COUNT * IMAGE_SIZE
  101. }
  102. }
  103. class MyData(var path: String, var index: Int)
  104. private fun readAllImage(context: Context): ArrayList<MyData> {
  105. val photos = ArrayList<MyData>()
  106. //读取所有图片
  107. val cursor = context.contentResolver.query(
  108. MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null
  109. )
  110. var index = 0
  111. while (cursor!!.moveToNext()) {
  112. //路径 uri
  113. val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
  114. //图片名称
  115. //val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
  116. //图片大小
  117. //val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))
  118. photos.add(MyData(path, index++))
  119. }
  120. cursor.close()
  121. return photos
  122. }
  123. }

避免使用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

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/435997
推荐阅读
相关标签
  

闽ICP备14008679号