码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Android GridLayoutManager Glide批量加载Bitmap绘制Canvas画在RecyclerView,Kotlin(a)


    Android GridLayoutManager Glide批量加载Bitmap绘制Canvas画在RecyclerView,Kotlin(a)

    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.graphics.Bitmap
    3. import android.graphics.Canvas
    4. import android.graphics.drawable.Drawable
    5. import android.os.Bundle
    6. import android.provider.MediaStore
    7. import android.util.AttributeSet
    8. import android.util.Log
    9. import android.view.LayoutInflater
    10. import android.view.View
    11. import android.view.ViewGroup
    12. import android.widget.TextView
    13. import androidx.appcompat.app.AppCompatActivity
    14. import androidx.core.content.ContextCompat
    15. import androidx.lifecycle.lifecycleScope
    16. import androidx.recyclerview.widget.GridLayoutManager
    17. import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
    18. import androidx.recyclerview.widget.LinearLayoutManager
    19. import androidx.recyclerview.widget.RecyclerView
    20. import com.bumptech.glide.request.target.CustomTarget
    21. import com.bumptech.glide.request.transition.Transition
    22. import com.google.android.material.imageview.ShapeableImageView
    23. import kotlinx.coroutines.Dispatchers
    24. import kotlinx.coroutines.launch
    25. import kotlinx.coroutines.withContext
    26. class MainActivity : AppCompatActivity() {
    27. companion object {
    28. const val TAG = "fly"
    29. const val VIEW_TYPE = 0
    30. const val GROUP_TYPE = 1
    31. const val SPAN_COUNT = 16
    32. }
    33. override fun onCreate(savedInstanceState: Bundle?) {
    34. super.onCreate(savedInstanceState)
    35. setContentView(R.layout.activity_main)
    36. val rv = findViewById(R.id.rv)
    37. val layoutManager = GridLayoutManager(this, SPAN_COUNT)
    38. layoutManager.orientation = LinearLayoutManager.VERTICAL
    39. rv.layoutManager = layoutManager
    40. val adapter = MyAdapter(this)
    41. rv.adapter = adapter
    42. rv.setHasFixedSize(true)
    43. layoutManager.spanSizeLookup = object : SpanSizeLookup() {
    44. override fun getSpanSize(position: Int): Int {
    45. return SPAN_COUNT
    46. }
    47. }
    48. lifecycleScope.launch(Dispatchers.IO) {
    49. val items = readAllImage(this@MainActivity)
    50. val lists: ArrayList = sliceDataList(items)
    51. withContext(Dispatchers.Main) {
    52. adapter.dataChanged(lists)
    53. }
    54. }
    55. }
    56. class MyAdapter : RecyclerView.Adapter<MyVH> {
    57. private var mItems = arrayListOf()
    58. private var mContext: Context? = null
    59. constructor(ctx: Context) {
    60. this.mContext = ctx
    61. }
    62. fun dataChanged(items: ArrayList<AdapterData>) {
    63. this.mItems = items
    64. notifyDataSetChanged()
    65. }
    66. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyVH {
    67. return when (viewType) {
    68. GROUP_TYPE -> {
    69. val view = LayoutInflater.from(mContext!!).inflate(android.R.layout.simple_list_item_1, parent, false)
    70. view.setBackgroundColor(ContextCompat.getColor(mContext!!, android.R.color.holo_orange_light))
    71. MyVH(view)
    72. }
    73. else -> {
    74. val view = BatchBitmapView(mContext!!)
    75. MyVH(view)
    76. }
    77. }
    78. }
    79. override fun getItemCount(): Int {
    80. return mItems.size
    81. }
    82. override fun getItemViewType(position: Int): Int {
    83. return mItems[position].type
    84. }
    85. override fun onBindViewHolder(holder: MyVH, position: Int) {
    86. Log.d(TAG, "onBindViewHolder $position")
    87. when (getItemViewType(position)) {
    88. GROUP_TYPE -> {
    89. holder.itemView.findViewById(android.R.id.text1).text = "$position GROUP"
    90. }
    91. else -> {
    92. (holder.itemView as BatchBitmapView).setRowBitmapData(mItems[position].mediaData, position)
    93. }
    94. }
    95. }
    96. }
    97. class MyVH : RecyclerView.ViewHolder {
    98. constructor(itemView: View) : super(itemView) {
    99. }
    100. }
    101. class AdapterData(var type: Int = GROUP_TYPE) {
    102. var mediaData: ArrayList? = null
    103. }
    104. class MediaData(var path: String, var index: Int)
    105. private fun sliceDataList(data: ArrayList<MediaData>): ArrayList {
    106. var k: Int
    107. val lists = ArrayList()
    108. for (i in data.indices step BatchBitmapView.ROW_SIZE) {
    109. lists.add(AdapterData(GROUP_TYPE))
    110. val temp = ArrayList()
    111. k = 0
    112. for (j in 0 until BatchBitmapView.ROW_SIZE) {
    113. k = i + j
    114. if (k >= data.size) {
    115. break
    116. }
    117. temp.add(data[k])
    118. }
    119. val adapterData = AdapterData(VIEW_TYPE)
    120. adapterData.mediaData = temp
    121. lists.add(adapterData)
    122. }
    123. return lists
    124. }
    125. private fun readAllImage(context: Context): ArrayList {
    126. val photos = ArrayList()
    127. //读取所有图片
    128. val cursor = context.contentResolver.query(
    129. MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null
    130. )
    131. var index = 0
    132. while (cursor!!.moveToNext()) {
    133. //路径 uri
    134. val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
    135. //图片名称
    136. //val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
    137. //图片大小
    138. //val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))
    139. photos.add(MediaData(path, index++))
    140. }
    141. cursor.close()
    142. return photos
    143. }
    144. }
    145. class BatchBitmapView : ShapeableImageView {
    146. private val mData = mutableListOf()
    147. private val mScreenWidth = resources.displayMetrics.widthPixels
    148. private val mTargets = mutableListOf>()
    149. private var mContext: Context? = null
    150. companion object {
    151. const val TAG = "BatchBitmapView"
    152. const val ROW_SIZE = MainActivity.SPAN_COUNT //一行多少个bitmap
    153. const val IMAGE_SIZE = 50 //每个小格子图片的尺寸
    154. }
    155. constructor(
    156. ctx: Context,
    157. attributeSet: AttributeSet? = null,
    158. defStyleAttr: Int = 0
    159. ) : super(ctx, attributeSet, defStyleAttr) {
    160. mContext = ctx
    161. }
    162. fun setRowBitmapData(rows: ArrayList<MainActivity.MediaData>?, position: Int) {
    163. Log.d(TAG, "setRowBitmapData $position")
    164. mData.clear()
    165. Log.d(TAG, "mTargets.size=${mTargets.size}")
    166. mTargets.forEach {
    167. GlideApp.with(context).clear(it) //如果不清除,会发生有些图错放位置。
    168. }
    169. mTargets.clear() //mTargets上下滑动列表会越来越大,清空,一直保持ROW_SIZE.
    170. rows?.forEachIndexed { index, data ->
    171. val target = object : CustomTarget() {
    172. override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
    173. val bean = DataBean(resource)
    174. mData.add(bean)
    175. postInvalidate()
    176. }
    177. override fun onLoadCleared(placeholder: Drawable?) {
    178. }
    179. }
    180. GlideApp.with(mContext!!)
    181. .asBitmap()
    182. .centerCrop()
    183. .override(IMAGE_SIZE)
    184. .load(data.path)
    185. .into(target)
    186. mTargets.add(target)
    187. }
    188. }
    189. override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    190. super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    191. setMeasuredDimension(mScreenWidth, IMAGE_SIZE)
    192. }
    193. override fun onDraw(canvas: Canvas) {
    194. super.onDraw(canvas)
    195. mData.forEachIndexed { index, dataBean ->
    196. canvas.save()
    197. val left = (mScreenWidth / ROW_SIZE) * index
    198. canvas.drawBitmap(dataBean.bitmap, left.toFloat(), 0f, null)
    199. canvas.restore()
    200. }
    201. }
    202. data class DataBean(val bitmap: Bitmap)
    203. }

    有一个遗留问题,每行加载16张图片,以行为原子单位。后面可以考虑另外一种实现,group分组标签单独占一行,图片可以一大片一大片的占据多行,每行16张。

    Android Glide自定义AppCompatImageView切分成若干小格子,每个小格子onDraw绘制Bitmap,Kotlin(1)_android appcompatimageview-CSDN博客文章浏览阅读1.2k次,点赞18次,收藏21次。垂直方向的RecyclerView,每行一个AppCompatImageView,每个AppCompatImageView被均匀切割成n个小格子, 每个小格子通过Glide加载出来Bitmap,然后onDraw绘制整行。//读取所有图片!//路径 uri//图片名称//图片大小= null,const val ROW_SIZE = 16 //一行多少个bitmap。_android appcompatimageviewhttps://zhangphil.blog.csdn.net/article/details/134519527Android GridLayoutManager SpanSizeLookup dynamic set grid cell column count,Kotlin-CSDN博客文章浏览阅读575次,点赞6次,收藏7次。Android RecyclerView的StaggeredGridLayoutManager实现交错排列的子元素分组先看实现的结果如图:设计背景:现在的产品对设计的需求越来越多样化,如附录文章2是典型的联系人分组RecyclerView,子元素排列到一个相同的组,但是有些时候,UI要求把这些元素不是垂直方向的,而是像本文开头的图中所示样式排列,这就需要用StaggeredGridLayoutMa_staggeredgridlayoutmanager。https://blog.csdn.net/zhangphil/article/details/137694645Android RecyclerView性能优化及Glide流畅加载图片丢帧率低的一种8宫格实现,Kotlin-CSDN博客文章浏览阅读690次,点赞26次,收藏11次。【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。现在结合他人的代码加以修改,给出一个以原始图形中心为原点,修剪图片为头像的工具类,此类可以直接在布局文件中加载使用,比。文章浏览阅读670次。https://blog.csdn.net/zhangphil/article/details/137653692

  • 相关阅读:
    谈谈悲观锁
    多目标果蝇算法及其MATLAB实现
    分享insert into select遇到的死锁问题(项目实战)
    2022年比若依更香的开源项目
    Arduino与Proteus仿真-WiFi网络仿真环境搭建
    S6---ZYNQ硬件板级原理图实战导学
    工程化分类面试题
    模型效果测试
    spring实现基于注解的@Bean配置原理
    大数据基础
  • 原文地址:https://blog.csdn.net/zhangphil/article/details/137823405
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号