Android Coil3缩略图、默认占位图placeholder、error加载错误显示,Kotlin(1)

Android Coil3缩略图、默认占位图placeholder、error加载错误显示,Kotlin(1)

 

 

    implementation("io.coil-kt.coil3:coil-core:3.1.0")implementation("io.coil-kt.coil3:coil-network-okhttp:3.1.0")

 

 

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

 

 

import android.content.ContentUris
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
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 coil3.ImageLoader
import coil3.disk.DiskCache
import coil3.memory.MemoryCache
import coil3.request.CachePolicy
import coil3.request.bitmapConfig
import android.os.Environment
import okio.Path.Companion.toPath
import java.io.Fileclass MainActivity : AppCompatActivity() {companion object {const val SPAN_COUNT = 8const val THUMB_WIDTH = 20const val THUMB_HEIGHT = 20}private var mImageLoader: ImageLoader? = nullprivate val TAG = "fly/MainActivity"override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val rv = findViewById<RecyclerView>(R.id.rv)initCoil()val layoutManager = GridLayoutManager(this, SPAN_COUNT)layoutManager.orientation = LinearLayoutManager.VERTICALval adapter = ImageAdapter(this, mImageLoader)rv.adapter = adapterrv.layoutManager = layoutManagerrv.setItemViewCacheSize(SPAN_COUNT * 2)rv.recycledViewPool.setMaxRecycledViews(0, SPAN_COUNT * 2)val ctx = thislifecycleScope.launch(Dispatchers.IO) {val imgList = readAllImage(ctx)val videoList = readAllVideo(ctx)Log.d(TAG, "readAllImage size=${imgList.size}")Log.d(TAG, "readAllVideo size=${videoList.size}")val lists = arrayListOf<MyData>()lists.addAll(imgList)lists.addAll(videoList)lists.shuffle()lifecycleScope.launch(Dispatchers.Main) {adapter.dataChanged(lists)}}}private fun initCoil() {val ctx = this//初始化加载器。mImageLoader = ImageLoader.Builder(this).memoryCachePolicy(CachePolicy.ENABLED).memoryCache(initMemoryCache()).diskCachePolicy(CachePolicy.ENABLED).diskCache(initDiskCache()).networkCachePolicy(CachePolicy.ENABLED).bitmapConfig(Bitmap.Config.ARGB_8888).components {//add(ThumbInterceptor())//add(ThumbMapper())//add(ImageKeyer())add(ThumbFetcher.Factory(ctx))//add(ThumbDecoder.Factory())}.build()Log.d(TAG, "memoryCache.maxSize=${mImageLoader?.memoryCache?.maxSize}")}private fun initMemoryCache(): MemoryCache {//内存缓存。val memoryCache = MemoryCache.Builder().maxSizeBytes(1024 * 1024 * 1024 * 1L) //1GB.build()return memoryCache}private fun initDiskCache(): DiskCache {//磁盘缓存。val diskCacheFolder = Environment.getExternalStorageDirectory()val diskCacheName = "coil_disk_cache"val cacheFolder = File(diskCacheFolder, diskCacheName)if (cacheFolder.exists()) {Log.d(TAG, "${cacheFolder.absolutePath} exists")} else {if (cacheFolder.mkdir()) {Log.d(TAG, "${cacheFolder.absolutePath} create OK")} else {Log.e(TAG, "${cacheFolder.absolutePath} create fail")}}val diskCache = DiskCache.Builder().maxSizeBytes(1024 * 1024 * 1024 * 2L) //2GB.directory(cacheFolder.absolutePath.toPath()).build()Log.d(TAG, "cache folder = ${diskCache.directory.toFile().absolutePath}")return diskCache}class MyData(var path: String, var uri: Uri)private fun readAllImage(ctx: Context): ArrayList<MyData> {val photos = ArrayList<MyData>()//读取所有图val cursor = ctx.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null)while (cursor!!.moveToNext()) {//路径val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA))val id = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)val imageUri: Uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getLong(id))//名称//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, imageUri))}cursor.close()return photos}private fun readAllVideo(context: Context): ArrayList<MyData> {val videos = ArrayList<MyData>()//读取视频Videoval cursor = context.contentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,null,null,null,null)while (cursor!!.moveToNext()) {//路径val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))val id = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)val videoUri: Uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getLong(id))//名称//val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME))//大小//val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE))videos.add(MyData(path, videoUri))}cursor.close()return videos}
}

 

 

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import android.util.Size
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.RecyclerView
import coil3.Image
import coil3.ImageLoader
import coil3.asImage
import coil3.memory.MemoryCache
import coil3.request.ImageRequest
import coil3.request.SuccessResult
import coil3.request.target
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContextclass ImageAdapter : RecyclerView.Adapter<ImageHolder> {private var mCtx: Context? = nullprivate var mImageLoader: ImageLoader? = nullprivate var mViewSize = 0private var mPlaceholderImage: Image? = nullprivate var mErrorBmp: Bitmap? = nullprivate val TAG = "fly/ImageAdapter"constructor(ctx: Context, il: ImageLoader?) : super() {mCtx = ctxmImageLoader = ilmViewSize = mCtx!!.resources.displayMetrics.widthPixels / MainActivity.SPAN_COUNTmPlaceholderImage = BitmapFactory.decodeResource(mCtx!!.resources, android.R.drawable.ic_menu_gallery).asImage()mErrorBmp = BitmapFactory.decodeResource(mCtx!!.resources, android.R.drawable.stat_notify_error)}private var mItems = ArrayList<MainActivity.MyData>()fun dataChanged(items: ArrayList<MainActivity.MyData>) {this.mItems = itemsnotifyDataSetChanged()}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageHolder {val view = MyIV(mCtx!!, mViewSize)return ImageHolder(view)}override fun getItemCount(): Int {return mItems.size}override fun onBindViewHolder(holder: ImageHolder, position: Int) {val data = mItems[position]val thumbItem = ThumbItem(uri = data.uri, path = data.path)val thumbMemoryCacheKey = MemoryCache.Key(thumbItem.toString())val thumbMemoryCache = getMemoryCache(thumbMemoryCacheKey)val imageItem = ImageItem(uri = data.uri, path = data.path)val imageMemoryCacheKey = MemoryCache.Key(imageItem.toString())val imageMemoryCache = getMemoryCache(imageMemoryCacheKey)var isHighQuality = falseif (thumbMemoryCache == null && imageMemoryCache == null) {(mCtx as AppCompatActivity).lifecycleScope.launch(Dispatchers.IO) {var bmp: Bitmap?try {bmp = mCtx!!.contentResolver.loadThumbnail(thumbItem.uri!!,Size(MainActivity.THUMB_WIDTH, MainActivity.THUMB_HEIGHT),null)mImageLoader?.memoryCache?.set(thumbMemoryCacheKey, MemoryCache.Value(bmp.asImage()))} catch (e: Exception) {Log.e(TAG, "loadThumbnail e=$e $thumbItem")bmp = mErrorBmp}withContext(Dispatchers.Main) {if (!isHighQuality) {holder.image.setImageBitmap(bmp)}}}}var imgPlaceholder = mPlaceholderImageif (thumbMemoryCache != null) {imgPlaceholder = thumbMemoryCache.image}val imageReq = ImageRequest.Builder(mCtx!!).data(mItems[position].uri).memoryCacheKey(imageMemoryCacheKey).size(mViewSize).target(holder.image).placeholder(imgPlaceholder).listener(object : ImageRequest.Listener {override fun onSuccess(request: ImageRequest, result: SuccessResult) {isHighQuality = trueLog.d(TAG, "image onSuccess ${result.dataSource} $imageItem ${calMemoryCache()}")}}).build()mImageLoader?.enqueue(imageReq)}private fun getMemoryCache(key: MemoryCache.Key): MemoryCache.Value? {return mImageLoader?.memoryCache?.get(key)}private fun calMemoryCache(): String {return "${mImageLoader?.memoryCache?.size} / ${mImageLoader?.memoryCache?.maxSize}"}
}class ImageHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {var image = itemView as MyIV
}class MyIV : AppCompatImageView {companion object {const val TAG = "fly/MyIV"}private var mSize = 0private var mCtx: Context? = nullconstructor(ctx: Context, size: Int) : super(ctx) {mCtx = ctxmSize = sizescaleType = ScaleType.CENTER_CROP}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)setMeasuredDimension(mSize, mSize)}
}

 

 

 

import android.net.Uriopen class Item {companion object {const val THUMB = 0const val IMG = 1}var uri: Uri? = nullvar path: String? = nullvar lastModified = 0Lvar width = 0var height = 0var position = -1var type = -1  //0,缩略图。 1,正图image。-1,未知。override fun toString(): String {return "Item(uri=$uri, path=$path, lastModified=$lastModified, width=$width, height=$height, position=$position, type=$type)"}
}

 

 

import android.net.Uriclass ImageItem : Item {constructor(uri: Uri, path: String, time: Long = 0, width: Int = 0, height: Int = 0, position: Int = 0) {this.uri = urithis.path = paththis.lastModified = timethis.width = widththis.height = heightthis.position = positionthis.type = IMG}
}

 

 

import android.net.Uriclass ThumbItem : Item {constructor(uri: Uri, path: String, time: Long = 0, width: Int = 0, height: Int = 0, position: Int = 0) {this.uri = urithis.path = paththis.lastModified = timethis.width = widththis.height = heightthis.position = positionthis.type = THUMB}
}

 

 

 

 

遗留问题:

1、在bind里面开启协程加载小缩略图不是很好,应该模块化改造。最好使用Coil的Fetcher加载缩略图。

2、现在分别使用缩略图内存缓存和正图内存缓存,感觉应该可以合并,只使用一套内存缓存。

 

 

 

Android Coil 3定制ImageRequest请求体data及内存复用,Kotlin-CSDN博客文章浏览阅读689次,点赞17次,收藏10次。Coil是专门针对Android平台上的Kotlin语言特性设计,这不像Glide,Glide的核心框架语言是Java。Coil实现看更细颗粒度的内存、磁盘缓存的客制化设置。扩大了内存,但跑起来发现设置后内存还是比较小(约300mb),这是不够的,需要通过其他配置方式扩大内存空间。3、app跑起来后,没有在当前app的硬盘缓存空间发现图片解码后的磁盘文件缓存痕迹。遗留问题,配置的disk cache似乎没有work,指定的磁盘缓存文件路径生成是生成了,但是app跑起来运行后(图正常显示),里面是空的。 https://blog.csdn.net/zhangphil/article/details/145737643

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/896091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DeepSeek 助力 Vue 开发:打造丝滑的 键盘快捷键(Keyboard Shortcuts)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

uniapp引入uview组件库(可以引用多个组件)

第一步安装 npm install uview-ui2.0.31 第二步更新uview npm update uview-ui 第三步在main.js中引入uview组件库 第四步在uni.scss中引入import "uview-ui/theme.scss"样式 第五步在文件中使用组件

Jmeter进阶篇(34)如何解决jmeter.save.saveservice.timestamp_format=ms报错?

问题描述 今天使用Jmeter完成压测执行,然后使用命令将jtl文件转换成html报告时,遇到了报错! 大致就是说jmeter里定义了一个jmeter.save.saveservice.timestamp_format=ms的时间格式,但是jtl文件中的时间格式不是标准的这个ms格式,导致无法正常解析。对于这个问题,有如下…

React 低代码项目:网络请求与问卷基础实现

&#x1f35e;吐司问卷&#xff1a;网络请求与问卷基础实现 Date: February 10, 2025 Log 技术要点&#xff1a; HTTP协议XMLHttpRequest、fetch、axiosmock.js、postmanWebpack devServer 代理、craco.js 扩展 webpackRestful API 开发要点&#xff1a; 搭建 mock 服务 …

安装海康威视相机SDK后,catkin_make其他项目时,出现“libusb_set_option”错误的解决方法

硬件&#xff1a;雷神MIX G139H047LD 工控机 系统&#xff1a;ubuntu20.04 之前运行某项目时&#xff0c;处于正常状态。后来由于要使用海康威视工业相机&#xff08;型号&#xff1a;MV-CA013-21UC&#xff09;&#xff0c;便下载了并安装了该相机的SDK&#xff0c;之后运行…

人工智能之自动驾驶技术体系

自动驾驶技术体系 自动驾驶技术是人工智能在交通领域的重要应用&#xff0c;旨在通过计算机视觉、传感器融合、路径规划等技术实现车辆的自主驾驶。自动驾驶不仅能够提高交通效率&#xff0c;还能减少交通事故和环境污染。本文将深入探讨自动驾驶的技术体系&#xff0c;包括感…

浅谈模组-相机鬼像

一&#xff0e;前言 在成像中&#xff0c;我们常常会遇到肉眼观测的真实世界中&#xff0c;不存在的异常光影出现在画面中&#xff0c;并伴有各种颜色&#xff0c;我们将这个物体称为鬼像。某些鬼像可能会对图像产生美感的体验&#xff0c;但是大多数的鬼像都会对图像的质量以…

vmware虚拟机Ubuntu Desktop系统怎么和我的电脑相互复制文件、内容

1、先安装vmware workstation 17 player&#xff0c;然后再安装Ubuntu Desktop虚拟机&#xff0c;然后再安装vmware tools&#xff0c;具体可以参考如下视频&#xff1a; VMware虚拟机与主机实现文件共享&#xff0c;其实一点也不难_哔哩哔哩_bilibili 2、本人亲自试过了&…

Spring Boot项目中解决跨域问题(四种方式)

目录 一&#xff0c;跨域产生的原因二&#xff0c;什么情况下算跨域三&#xff0c;实际演示四&#xff0c;解决跨域的方法 1&#xff0c;CrossOrigin注解2&#xff0c;添加全局过滤器3&#xff0c;实现WebMvcConfigurer4&#xff0c;Nginx解决跨域5&#xff0c;注意 开发项目…

Oracle JDK、Open JDK zulu下载地址

一、Oracle JDK https://www.oracle.com/java/technologies/downloads/ 刚进去是最新的版本&#xff0c;往下滑可以看到老版本 二、Open JDK的 Azul Zulu https://www.azul.com/downloads/ 直接可以选版本等选项卡

软件测试:1、单元测试

1. 单元测试的基本概念 单元&#xff08;Unit&#xff09;&#xff1a;软件系统的基本组成单位&#xff0c;可以是函数、模块、方法或类。 单元测试&#xff08;Unit Testing&#xff09;&#xff1a;对软件单元进行的测试&#xff0c;验证代码的正确性、规范性、安全性和性能…

Leetcode.264 丑数 II

题目链接 Leetcode.264 丑数 II mid 题目描述 给你一个整数 n n n &#xff0c;请你找出并返回第 n n n 个 丑数 。 丑数 就是质因子只包含 2 2 2、 3 3 3 和 5 5 5 的正整数。 示例1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;12 解释&#xff1a;[1, 2, 3,…

瑞芯微RV1126部署YOLOv8全流程:环境搭建、pt-onnx-rknn模型转换、C++推理代码、错误解决、优化、交叉编译第三方库

目录 1 环境搭建 2 交叉编译opencv 3 模型训练 4 模型转换 4.1 pt模型转onnx模型 4.2 onnx模型转rknn模型 4.2.1 安装rknn-toolkit 4.2.2 onn转成rknn模型 5 升级npu驱动 6 C++推理源码demo 6.1 原版demo 6.2 增加opencv读取图片的代码 7 交叉编译x264 ffmepg和op…

【Python爬虫(32)】从单飞 to 团战:Python多线程爬虫进化史

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…

C#初级教程(1)——C# 与.NET 框架:探索微软平台编程的强大组合

图片来源&#xff1a; https://www.lvhang.site/docs/dotnettimeline 即梦AI - 一站式AI创作平台 一、历史发展脉络 在早期的微软平台编程中&#xff0c;常用的编程语言有 Visual Basic、C、C。到了 20 世纪 90 年代末&#xff0c;Win32 API、MFC&#xff08;Microsoft Found…

【接口封装】——13、登录窗口的标题栏内容设置

解释&#xff1a; 1、封装内容&#xff1a;图标、文本内容、宽度 2、ui.iconLabel&#xff1a;在UI文件中的自定义命名 3、引入头文件&#xff1a;#include<qpixmap.h> 函数定义&#xff1a; #pragma once#include <QWidget> #include "ui_TitleBar.h"cl…

DeepSeek全生态接入指南:官方通道+三大云平台

DeepSeek全生态接入指南&#xff1a;官方通道三大云平台 一、官方资源入口 1.1 核心交互平台 &#x1f5a5;️ DeepSeek官网&#xff1a; https://chat.deepseek.com/ &#xff08;体验最新对话模型能力&#xff09; 二、客户端工具 OllamaChatboxCherry StudioAnythingLLM …

web安全:跨站请求伪造 (CSRF)

跨站请求伪造 (CSRF) ​ 跨站请求伪造&#xff08;CSRF&#xff0c;Cross-Site Request Forgery&#xff09; 是一种网络攻击方式&#xff0c;攻击者诱使受害者在未经其授权的情况下执行特定操作。CSRF 利用受害者已登录的身份和浏览器自动发送的认证信息&#xff08;如 Cooki…

前端ES面试题及参考答案

目录 let/const 与 var 的区别?TDZ 是什么? 箭头函数与普通函数的区别?箭头函数能否作为构造函数? 模板字符串的嵌套表达式和标签模板用法? 解构赋值的应用场景及对象 / 数组解构差异? 函数参数默认值的生效条件及暂时性死区问题? 展开运算符(...)在数组 / 对象中…

Windows 图形显示驱动开发-查询 WDDM(3.2) 功能支持和启用

查询 Windows 显示驱动程序模型 (WDDM) 功能的支持和启用。 其中介绍了&#xff1a; 用户模式和内核模式显示驱动程序&#xff08;UMD 和 KMD&#xff09;如何查询 OS&#xff0c;以确定 WDDM 功能在系统上是否受支持和已启用。 OS 如何确定驱动程序是否支持特定的 WDDM 功能…