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.pswp.cn/news/896091.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/896091.shtml
英文地址,請注明出處:http://en.pswp.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 功能…