一、介绍
在Android,资源的呈现主要有三大形式:文字、图片、视频。图片有分为本地资源和网络资源。
网络资源需要通过下载然后绑定到ImageView中。
在前期我们使用的图片加载框架如:picasso、gliade、imageload。这些框架都可以满足我们正常的图片加载到显示,且这些框架都是通过Java语言开发出来。接下来,我将介绍一款以kotlin语言开发的框架Coil。
Coil的介绍
一、介绍
是一个 Android 图片加载库,通过Kotlin协程的方式加载图片
优势
-
更快:
Coil 在性能上有很多优化,包括内存缓存和磁盘缓存,把缩略图存保存在内存中,循环利用 bitmap,自动暂停和取消图片网络请求等。 -
更轻量级:
Coil 的包体很小,很多API依赖项目中的其他模块的框架(如:前提是你的 APP 里面集成了 OkHttp 和 Coroutines),Coil 和 Picasso 的方法数差不多,相比 Glide 和 Fresco 要轻量很多,kotlin的语法支持,代码更简洁。 -
更容易使用:
Coil 的 API 充分利用了 Kotlin 语言的新特性,简化和减少了很多样板代码,采用了类方法扩展,常用的API已通过ImageViews扩展到了ImageView中,可以直接使用 -
更流行更新:
Coil 首选 Kotlin 语言开发并且使用包含 Coroutines, OkHttp, Okio 和 AndroidX Lifecycles 在内最流行的开源库,很好的兼容最新Jetpack组件。
2、集成
io.coil-kt:coil-base : 基础组件,提供了基本的图片请求、图片解码、图片缓存等
io.coil-kt:coil : 默认组件,依赖于io.coil-kt:coil-base,提供了 Coil 类的单例对象以及 ImageViews 相关的扩展函数
io.coil-kt:coil-gif : 用于支持解码 GIFs
io.coil-kt:coil-svg : 用于支持解码 SVG
io.coil-kt:coil-video : 包含两个 fetchers 用于支持读取和解码 任何 Android 的支持的视频格式 的视频帧
//依赖库
implementation("io.coil-kt:coil:1.2.2")
// //选择添加
implementation("io.coil-kt:coil-gif:1.2.2")//支持GIF
implementation("io.coil-kt:coil-svg:1.2.2")//支持SVG
implementation("io.coil-kt:coil-video:1.2.2")//支持Video
3、使用
由于在kotlin支持方法的扩展,所以ImageViews已支持提供了load方法,默认只需要传入图片地址即可。
@JvmSynthetic
inline fun ImageView.load(
uri: String?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
url: HttpUrl?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(url, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
uri: Uri?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
file: File?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(file, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
@DrawableRes drawableResId: Int,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(drawableResId, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
drawable: Drawable?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(drawable, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
bitmap: Bitmap?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(bitmap, imageLoader, builder)
load支持的请求类型
uri: String:图片地址url: HttpUrl:封装好的请求urluri: Uri:Uri的资源封装file: File:文件类型drawableResId: Int:资源iddrawable: Drawable:drawable对象bitmap: Bitmap:bitmap对象
加载load,返回一个Disposable,Disposable是IPC的接口,如下
interface Disposable {
/**
* Returns true if the request is complete or cancelling.
*/
val isDisposed: Boolean
/**
* Cancels any in progress work and frees any resources associated with this request. This method is idempotent.
*/
fun dispose()
/**
* Suspends until any in progress work completes.
*/
@ExperimentalCoilApi
suspend fun await()
}
通过Disposable可以判断是否完成,主动调用以及等待。
扩展:
Coil不仅提供了默认的加载,还可以扩展其他。
inline fun ImageView.load(
uri: String?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
通过源码可以看到,requestbuild是支持扩展的。默认后面是{},可以在{}体中调用build中方法。
bind.image.load(url){
//build 扩展体
crossfade(true) //渐进渐出
placeholder(R.mipmap.ic_launcher) //加载中占位图
error(R.mipmap.ic_launcher) //加载失败占位图
allowHardware(true)//硬件加速
allowRgb565(true)//支持565格式
lifecycle(lifecycle)//生命周期关联
var default= DefaultRequestOptions()
defaults(default)
}
包括网络缓存等,我们可以自己根据业务配置。如果你需要可以自定义一个ImageRequest.Build
ImageView高阶变换
Transformation(图片变换)
- BlurTransformation() : 高斯模糊变换
- CircleCropTransformation() : 圆形裁剪变换
- GrayscaleTransformation() : 灰度变换
- RoundedCornersTransformation() : 圆角变换
高阶图片变形通过transformations()来完成,支持多样式。
BlurTransformation() : 高斯模糊变换
class BlurTransformation @JvmOverloads constructor(
private val context: Context,
private val radius: Float = DEFAULT_RADIUS,
private val sampling: Float = DEFAULT_SAMPLING
)
init {
require(radius in 0.0..25.0) { "radius must be in [0, 25]." }
require(sampling > 0) { "sampling must be > 0." }
}
1.radius:模糊半径范围在0到25之间,数值越大,模糊越深
2.sampling:采样率必须大于,这个值是进行缩放图片大小的,(0,1),图片是越来越大,[1,++oo),图片是越来越小
GrayscaleTransformation() : 灰度变换
这个主要把彩色的图片置灰,常见特殊忌日,可以设置,利用了ColorMatrixColorFilter对颜色进行过滤。
RoundedCornersTransformation:圆角角度设置
class RoundedCornersTransformation( @Px private val topLeft: Float = 0f, @Px private val topRight: Float = 0f, @Px private val bottomLeft: Float = 0f, @Px private val bottomRight: Float = 0f )
A=topleft ,B=topright,C=bottomLeft,D=bottomRight。
如果只传一个参数,默认就是四个角的角度,角度默认值是0,任何角的坐标都是(x,y),所以你设置角度无法只设置一个叫的一半。
如果想了解view的角度自定义,可以查看:
Android ImageView 四个角自定义角度,以及角度的变换_addroundrect_蜗牛、Z的博客-CSDN博客
参数扩展
有人用习惯了Glide或者imageload,会发现,可以通过配置来管理加载中,加载失败等标识,Coil同样也支持,只是通过build提供的方法来完成。
inline fun ImageView.load(
uri: String?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
bind.load.setOnClickListener {
bind.image.load("https://img-blog.csdnimg.cn/3a4114c916904ecbadb7a71b77294eef.gif"){
crossfade(true) //渐进渐出
placeholder(R.mipmap.ic_launcher) //加载中占位图
error(R.mipmap.ic_launcher) //加载失败占位图
allowHardware(true)//硬件加速
// transformations(BlurTransformation(context,25f,2f))
//圆角
transformations(RoundedCornersTransformation(90f,0f,0f,0f))
}
}
有人会有疑问:
builder: ImageRequest.Builder.() -> Unit = {}
这个写法,是返回builder类,通过指向类的内部方法来完成,最后返回build。
小试牛刀:
class Build {
var msgs: String? = ""
fun setMsg(msg: String) = apply {
this.msgs = msg
}
}
class TestBody(var build: Build.() -> Unit = {}) {
fun log() {
var mBuild = Build()
mBuild.apply(build)
mBuild.msgs?.let { MyLog.log(it) }
}
}
fun main() {
var test = TestBody({
setMsg("hello")
})
test.log()
}
build是一个block object的,我们可以通过Build对象apply方法copy这个对象。
GIF动态图片加载
Coil库不仅支持静态图片,也支持动态图片。只是动态和静态的解析对象不同ComponentRegistry在注册的时候,已提供了各种拦截与支持。
依赖库的引入:
implementation("io.coil-kt:coil-gif:1.2.2")//支持GIF
decoder目前提供两种方式:
api<28
/**
* A [Decoder] that uses [Movie] to decode GIFs.
*
* NOTE: Prefer using [ImageDecoderDecoder] on API 28 and above.
*/
class GifDecoder : Decoder
api>=28
/**
* A [Decoder] that uses [ImageDecoder] to decode GIFs, animated WebPs, and animated HEIFs.
*
* NOTE: Animated HEIF files are only supported on API 30 and above.
*/
@RequiresApi(28)
class ImageDecoderDecoder : Decoder
ImageLoad初始化:
注意GIFDecoder类的顶部注释:
NOTE: Prefer using ImageDecoderDecoder on API 28 and above.
//创建 gif ImageLoader 实例
val imageLoader = ImageLoader.Builder(applicationContext)
.componentRegistry {
if (SDK_INT >= 28) {
add(ImageDecoderDecoder(applicationContext))
} else {
add(GifDecoder())
}
}.build()
//设置全局唯一实例
Coil.setImageLoader(imageLoader)
这样我们就完成了Gif的ImageLoader的设置
如何监听图片加载:
ImageRequest提供了fun listener(listener: Listener?) 方法。
bind.image.load("https://img-blog.csdnimg.cn/3a4114c916904ecbadb7a71b77294eef.gif") {
listener(object : ImageRequest.Listener {
override fun onCancel(request: ImageRequest) {
super.onCancel(request)
}
override fun onSuccess(request: ImageRequest, metadata: ImageResult.Metadata) {
super.onSuccess(request, metadata)
}
override fun onError(request: ImageRequest, throwable: Throwable) {
super.onError(request, throwable)
}
override fun onStart(request: ImageRequest) {
super.onStart(request)
}
})
}
关于配置
上面都是通过方法设置配置参数,如何设置全局配置?需要我们自己去设置一个全局的 ImageLoader.Builder
val okHttpClient = OkHttpClient.Builder()
.cache(CoilUtils.createDefaultCache(this))
.build()
val imageLoader = ImageLoader.Builder(this)
.availableMemoryPercentage(0.5f)
.diskCachePolicy(CachePolicy.ENABLED) //磁盘缓策略 ENABLED、READ_ONLY、WRITE_ONLY、DISABLED
.crossfade(true) //淡入淡出
.okHttpClient { //设置okhttpClient实例
okHttpClient
}.build()
Coil.setImageLoader(imageLoader)
api介绍:
1.availableMemoryPercentage:
设置用于此 ImageLoader 的内存缓存和位图池的可用内存百分比,如果设置了百分百,那么cache将会失效
//源码
fun availableMemoryPercentage(@FloatRange(from = 0.0, to = 1.0) percent: Double) = apply {
require(percent in 0.0..1.0) { "Percent must be in the range [0.0, 1.0]." }
this.availableMemoryPercentage = percent
this.memoryCache = null
}
2.memoryCachePolicy :
内存缓存策略,有4中策略;
![](https://i-blog.csdnimg.cn/blog_migrate/62a46270a7667a829771ab8a36d6efb8.png)
3.diskCachePolicy:
磁盘缓存策略,方式和内存策略一致;
CachePolicy.ENABLED : 可读可写
CachePolicy.READ_ONLY : 只读
CachePolicy.WRITE_ONLY : 只写
CachePolicy.DISABLED : 不可读不可写,即禁用
短视频首帧
Coil还有特别特别项,支持短视频首帧图片加载。正常在项目中,如果该项目有短视频业务,在视频不播放的状态下,如何获取首帧图片?这个问题是很多公司思考的问题。
大公司在处理视频首帧都是通过服务器跑的。这样,在视频集在列表展示只处理单张图片即可。针对某一个视频,特别是小公司的时候,Coil明显很友好,可以在接收的范围类,降低技术和服务器的成本。
依赖库的引入:
implementation("io.coil-kt:coil-video:1.2.2")//支持Video
添加VideoDecoder
//创建 gif ImageLoader 实例
val imageLoader = ImageLoader.Builder(applicationContext)
.componentRegistry {
add(VideoFrameDecoder(context))
}.build()
//设置全局唯一实例
Coil.setImageLoader(imageLoader)
剩余的设置,都是通用设置,如果项目需要支持GIF、VideoFrame,可以同时支持进去,都是通过componentRegistry add添加,可以追加进去。