Android优化RecyclerView图片展示:Glide成堆加载批量Bitmap在RecyclerView成片绘制Canvas,Kotlin(b)

本文主要是介绍Android优化RecyclerView图片展示:Glide成堆加载批量Bitmap在RecyclerView成片绘制Canvas,Kotlin(b),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android优化RecyclerView图片展示:Glide成堆加载批量Bitmap在RecyclerView成片绘制Canvas,Kotlin(b)

对 Android GridLayoutManager Glide批量加载Bitmap绘制Canvas画在RecyclerView,Kotlin(a)-CSDN博客 改进,用Glide批量把Bitmap加载出来,然后在RecyclerView成片成堆的绘制Canvas,此种实现是RecyclerView加载多宫格图片展示,卡顿丢帧最低的一种实现,上下滑动流畅。

    <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 = 200Fval maxSizeMultiplier = 0.8Fval 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 * 2000Lbuilder.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.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.provider.MediaStore
import android.util.AttributeSet
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.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.google.android.material.imageview.ShapeableImageView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.ceil
import kotlin.math.roundToIntconst val COLUMN_COUNT = 16 // 一行多少个图片。
const val CHUNKED_SIZE = 100 // 一批/一大片总共有多少张图片。class MainActivity : AppCompatActivity() {companion object {const val TAG = "fly"const val VIEW_TYPE = 0 //图片区域。const val GROUP_TYPE = 1 //分组标签。}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val rv = findViewById<RecyclerView>(R.id.rv)val layoutManager = LinearLayoutManager(this)layoutManager.orientation = LinearLayoutManager.VERTICALrv.layoutManager = layoutManagerval adapter = MyAdapter(this)rv.adapter = adapterrv.setHasFixedSize(true)lifecycleScope.launch(Dispatchers.IO) {val items = readAllImage(this@MainActivity)items.reverse()val lists: ArrayList<AdapterData> = sliceDataList(items)withContext(Dispatchers.Main) {adapter.dataChanged(lists)}}}class MyAdapter : RecyclerView.Adapter<MyVH> {private var mItems = arrayListOf<AdapterData>()private var mContext: Context? = nullconstructor(ctx: Context) {this.mContext = ctx}fun dataChanged(items: ArrayList<AdapterData>) {this.mItems = itemsnotifyDataSetChanged()}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyVH {return when (viewType) {GROUP_TYPE -> {val view = LayoutInflater.from(mContext!!).inflate(android.R.layout.simple_list_item_1, parent, false)view.setBackgroundColor(ContextCompat.getColor(mContext!!, android.R.color.darker_gray))MyVH(view)}else -> {val view = BatchBitmapView(mContext!!)MyVH(view)}}}override fun getItemCount(): Int {return mItems.size}override fun getItemViewType(position: Int): Int {return mItems[position].type}override fun onBindViewHolder(holder: MyVH, position: Int) {Log.d(TAG, "onBindViewHolder $position")when (getItemViewType(position)) {GROUP_TYPE -> {holder.itemView.findViewById<TextView>(android.R.id.text1).text = "$position GROUP"}else -> {(holder.itemView as BatchBitmapView).setRowBitmapData(mItems[position].mediaData)}}}}class MyVH : RecyclerView.ViewHolder {constructor(itemView: View) : super(itemView) {}}class AdapterData(var type: Int = GROUP_TYPE, data: List<MediaData>? = null) {var mediaData: List<MediaData>? = data}class MediaData(var path: String, var index: Int)private fun sliceDataList(data: ArrayList<MediaData>): ArrayList<AdapterData> {val lists = ArrayList<AdapterData>()val chunks = data.chunked(CHUNKED_SIZE)chunks.forEach {lists.add(AdapterData(GROUP_TYPE))lists.add(AdapterData(VIEW_TYPE, it))}return lists}private fun readAllImage(context: Context): ArrayList<MediaData> {val photos = ArrayList<MediaData>()//读取所有图片val cursor = context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null)var index = 0while (cursor!!.moveToNext()) {//路径 urival 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(MediaData(path, index++))}cursor.close()return photos}
}class BatchBitmapView : ShapeableImageView {private val mData = mutableListOf<DataBean>()private val mScreenWidth = resources.displayMetrics.widthPixelsprivate val mTargets = mutableListOf<CustomTarget<Bitmap>>()private var mContext: Context? = nullprivate var mImageSize = 0 //每个小格子图片的尺寸,动态计算而变化。companion object {const val TAG = "BatchBitmapView"}constructor(ctx: Context,attributeSet: AttributeSet? = null,defStyleAttr: Int = 0) : super(ctx, attributeSet, defStyleAttr) {mContext = ctxmImageSize = (mScreenWidth.toFloat() / COLUMN_COUNT.toFloat()).roundToInt()}fun setRowBitmapData(rows: List<MainActivity.MediaData>?) {mData.clear()Log.d(TAG, "mTargets.size=${mTargets.size}")mTargets.forEach {GlideApp.with(mContext!!).clear(it) //如果不清除,会发生有些图错放位置。}mTargets.clear() //mTargets上下滑动列表会越来越大,清空,一直保持ROW_SIZE.rows?.forEachIndexed { index, data ->val target = object : CustomTarget<Bitmap>() {override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {val bean = DataBean(resource)mData.add(bean)postInvalidate()}override fun onLoadCleared(placeholder: Drawable?) {}}GlideApp.with(mContext!!).asBitmap().centerCrop().override(mImageSize).load(data.path).into(target)mTargets.add(target)}}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val rows = ceil(CHUNKED_SIZE.toFloat() / COLUMN_COUNT.toFloat()).toInt()setMeasuredDimension(mScreenWidth, mImageSize * rows)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)mData.forEachIndexed { index, dataBean ->//canvas.save()val left = mImageSize * (index % COLUMN_COUNT)val top = (index / COLUMN_COUNT) * mImageSizecanvas.drawBitmap(dataBean.bitmap, left.toFloat(), top.toFloat(), null)//canvas.restore()}}data class DataBean(val bitmap: Bitmap)
}

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

Android RecyclerView性能优化及Glide流畅加载图片丢帧率低的一种8宫格实现,Kotlin-CSDN博客文章浏览阅读695次,点赞26次,收藏11次。【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。现在结合他人的代码加以修改,给出一个以原始图形中心为原点,修剪图片为头像的工具类,此类可以直接在布局文件中加载使用,比。文章浏览阅读670次。https://blog.csdn.net/zhangphil/article/details/137653692Android GridLayoutManager SpanSizeLookup dynamic set grid cell column count,Kotlin-CSDN博客文章浏览阅读584次,点赞6次,收藏7次。Android RecyclerView的StaggeredGridLayoutManager实现交错排列的子元素分组先看实现的结果如图:设计背景:现在的产品对设计的需求越来越多样化,如附录文章2是典型的联系人分组RecyclerView,子元素排列到一个相同的组,但是有些时候,UI要求把这些元素不是垂直方向的,而是像本文开头的图中所示样式排列,这就需要用StaggeredGridLayoutMa_staggeredgridlayoutmanager。https://blog.csdn.net/zhangphil/article/details/137694645Android Glide load grid RecyclerView scroll smooth, high performance and ,Kotlin-CSDN博客文章浏览阅读709次,点赞18次,收藏13次。【代码】Android Paging 3,kotlin(1)在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬而未决:比如用户的头像,往往用户的头像是从服务器端读出的一个普通矩形图片,但是现在的设计一般要求在APP端的用户头像显示成圆形头像,那么此时虽然Glide可以加载,但加载出来的是一个矩形,如果要Glide_android 毛玻璃圆角。现在结合他人的代码加以修改,给出一个以原始图形中心为原点,修剪图片为头像的工具类,此类可以直接在布局文件中加载使用,比。文章浏览阅读670次。https://blog.csdn.net/zhangphil/article/details/137520793

这篇关于Android优化RecyclerView图片展示:Glide成堆加载批量Bitmap在RecyclerView成片绘制Canvas,Kotlin(b)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/917363

相关文章

Vue ElementUI中Upload组件批量上传的实现代码

《VueElementUI中Upload组件批量上传的实现代码》ElementUI中Upload组件批量上传通过获取upload组件的DOM、文件、上传地址和数据,封装uploadFiles方法,使... ElementUI中Upload组件如何批量上传首先就是upload组件 <el-upl

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

如何使用CSS3实现波浪式图片墙

《如何使用CSS3实现波浪式图片墙》:本文主要介绍了如何使用CSS3的transform属性和动画技巧实现波浪式图片墙,通过设置图片的垂直偏移量,并使用动画使其周期性地改变位置,可以创建出动态且具有波浪效果的图片墙,同时,还强调了响应式设计的重要性,以确保图片墙在不同设备上都能良好显示,详细内容请阅读本文,希望能对你有所帮助...

Python脚本实现图片文件批量命名

《Python脚本实现图片文件批量命名》这篇文章主要为大家详细介绍了一个用python第三方库pillow写的批量处理图片命名的脚本,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言源码批量处理图片尺寸脚本源码GUI界面源码打包成.exe可执行文件前言本文介绍一个用python第三方库pi

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图

spring-boot-starter-thymeleaf加载外部html文件方式

《spring-boot-starter-thymeleaf加载外部html文件方式》本文介绍了在SpringMVC中使用Thymeleaf模板引擎加载外部HTML文件的方法,以及在SpringBoo... 目录1.Thymeleaf介绍2.springboot使用thymeleaf2.1.引入spring

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

关于Spring @Bean 相同加载顺序不同结果不同的问题记录

《关于Spring@Bean相同加载顺序不同结果不同的问题记录》本文主要探讨了在Spring5.1.3.RELEASE版本下,当有两个全注解类定义相同类型的Bean时,由于加载顺序不同,最终生成的... 目录问题说明测试输出1测试输出2@Bean注解的BeanDefiChina编程nition加入时机总结问题说明

Deepseek使用指南与提问优化策略方式

《Deepseek使用指南与提问优化策略方式》本文介绍了DeepSeek语义搜索引擎的核心功能、集成方法及优化提问策略,通过自然语言处理和机器学习提供精准搜索结果,适用于智能客服、知识库检索等领域... 目录序言1. DeepSeek 概述2. DeepSeek 的集成与使用2.1 DeepSeek API