Skip to content

Commit 47e76dd

Browse files
committed
Merge remote-tracking branch 'origin/master'
# Conflicts: # README.md
2 parents d819e4c + 7b34879 commit 47e76dd

File tree

1 file changed

+242
-91
lines changed

1 file changed

+242
-91
lines changed

README.md

Lines changed: 242 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,253 @@
11
# ImageExt 参考Coil对Glide封装实现
22

3-
> 主要为ImageView添加扩展函数来简化常见图片加载api [![](https://jitpack.io/v/forJrking/ImageExt.svg)](https://jitpack.io/#forJrking/ImageExt)
3+
## 前言
44

5-
![img](img/img.gif)
5+
`glide``Google 官方`推荐的一款图片加载库,Coil是号称[Kotlin-first的Android图片加载库](https://juejin.cn/post/6844903913007611917) ,融合了kotlin特性、android最主流的技术和趋势,本篇我们主要分享如何用`kotlin``glide`封装的使用起来像`coil`一样。
66

7-
## 使用方法
7+
## 集成和API预览
88

9-
```groovy
10-
allprojects {
11-
repositories {
12-
...
13-
maven { url 'https://www.jitpack.io' }
14-
}
9+
1. ```groovy
10+
allprojects {
11+
repositories {
12+
maven { url 'https://www.jitpack.io' }
13+
}
14+
}
15+
```
16+
17+
2. ```groovy
18+
dependencies {
19+
implementation 'com.github.forJrking:ImageExt:0.0.2'
20+
}
21+
```
22+
23+
3. ```kotlin
24+
//配置全局占位图 错误图 非必须
25+
ImageOptions.DrawableOptions.setDefault {
26+
placeHolderResId = R.drawable.ic_launcher_background
27+
errorResId = R.color.gray
28+
}
29+
// URL
30+
imageView.load("https://www.example.com/image.jpg")
31+
// Resource
32+
imageView.load(R.drawable.image)
33+
//回调和进度监听
34+
imageView.load("https://www.example.com/image.jpg") {
35+
placeHolderResId = R.drawable.placeholder
36+
transformation = arrayOf(GrayscaleTransformation())
37+
progressListener { isComplete, percentage, bytesRead, totalBytes ->
38+
//加载进度
39+
}
40+
requestListener {
41+
onSuccess {
42+
}
43+
onFail {
44+
}
45+
}
46+
}
47+
```
48+
49+
4. 其他扩展函数和效果
50+
51+
```kotlin
52+
ImageView.loadImage(...)
53+
ImageView.loadProgressImage(...)
54+
ImageView.loadResizeImage(...)
55+
ImageView.loadGrayImage(...)
56+
ImageView.loadBlurImage(...)
57+
ImageView.loadBlurImage(...)
58+
ImageView.loadRoundCornerImage(...)
59+
ImageView.loadCircleImage(...)
60+
ImageView.loadBorderImage(...)
61+
```
62+
63+
![](https://files.catbox.moe/f27rwx.gif)
64+
65+
## 封装实现
66+
67+
首先看coil的调用方式采用了`kotlin`扩展函数形式,给ImageView增加了一个`load(url)`函数,然后其他占位图等配置通过DSL方式去设置。DSL如何学习和使用后面单独说。
68+
69+
第一步封装如下一个函数:
70+
71+
```kotlin
72+
/**模仿 coil DSL写法**/
73+
fun ImageView.load(load: Any?, options: (ImageOptions.() -> Unit)? = null) {
74+
ImageLoader.loadImage(ImageOptions(load).also(options))
1575
}
1676
```
1777

18-
```groovy
19-
dependencies {
20-
implementation 'com.github.forJrking:ImageExt:0.0.3'
78+
第二步封装配置类:
79+
80+
```kotlin
81+
/**
82+
* 图片加载库的配置,封装原始加载配置属性,进行转换
83+
*/
84+
class ImageOptions {
85+
/*** 加载原始资源*/
86+
var res: Any? = null
87+
/*** 显示容器*/
88+
var imageView: ImageView? = null
89+
/*** imageView存在的上下文或者fragment\activity*/
90+
var context: Any? = null
91+
get() {
92+
return field ?: imageView
93+
}
94+
/*** 加载占位图资源ID,如果placeholder是0表示没有占位图*/
95+
@DrawableRes
96+
var placeHolderResId = 0
97+
.... 省略其动画、错误图等等他属性
98+
var centerCrop: Boolean = false
99+
/*** 网络进度监听器*/
100+
var onProgressListener: OnProgressListener? = null
101+
/*** 加载监听*/
102+
var requestListener: OnImageListener? = null
103+
....省略缓存策略和优先级等等枚举
21104
}
22105
```
106+
107+
第三步 策略实现,由于要使用okhttp拦截器做进度监听,通过注解方式配置glide的网络下载器。
108+
23109
```kotlin
24-
//配置全局占位图 错误图 非必须
25-
ImageOptions.DrawableOptions.setDefault {
26-
placeHolderResId = R.drawable.ic_launcher_background
27-
errorResId = R.color.gray
28-
}
29-
//加载url 单独设置占位图
30-
iv_2.loadImage(url, placeHolder = R.color.blue)
31-
//模糊
32-
iv_3.loadBlurImage(url)
33-
//圆形
34-
iv_4.loadCircleImage(url)
35-
//边框
36-
iv_5.loadBorderImage(url, borderWidth = 10, borderColor = Color.RED)
37-
//黑白
38-
iv_6.loadGrayImage(url)
39-
//圆角
40-
iv_7.loadRoundCornerImage(url, radius = 10, type = ImageOptions.CornerType.ALL)
41-
//resize
42-
iv_8.loadResizeImage(url, width = 400, height = 800)
43-
//支持不需要全局占位图 单独分类管理
44-
val homeOptions = ImageOptions.DrawableOptions(placeHolderResId = R.drawable.home_holder,errorResId = R.drawable.error_holder)
45-
iv_8.load(url){
46-
drawableOptions = homeOptions
47-
}
48-
//监听回调结果
49-
iv_9.loadImage(url4, requestListener = {
50-
onSuccess {
51-
Toast.makeText(application, R.string.load_success, Toast.LENGTH_LONG).show()
52-
}
53-
onFail {
54-
Toast.makeText(application, R.string.load_failed, Toast.LENGTH_SHORT).show()
55-
}
56-
})
57-
//终极扩展 参数非常多必须使用可选参数方式调用
58-
iv_8.load(url1) {
59-
placeHolderResId = R.color.black
60-
transformation = arrayOf(GrayscaleTransformation())
61-
progressListener { isComplete, percentage, bytesRead, totalBytes ->
62-
//加载进度
63-
}
64-
requestListener {
65-
onSuccess {
110+
/**Glide策略封装*/
111+
object ImageLoader {
112+
fun loadImage(options: ImageOptions) {
113+
Preconditions.checkNotNull(options, "ImageConfigImpl is required")
114+
val context = options.context
115+
Preconditions.checkNotNull(context, "Context is required")
116+
Preconditions.checkNotNull(options.imageView, "ImageView is required")
117+
val requestsWith = glideRequests(context)
118+
//根据类型获取
119+
val glideRequest = when (options.res) {
120+
is String -> requestsWith.load(options.res as String)
121+
is Bitmap -> requestsWith.load(options.res as Bitmap)
122+
is Drawable -> requestsWith.load(options.res as Drawable)
123+
is Uri -> requestsWith.load(options.res as Uri)
124+
is URL -> requestsWith.load(options.res as URL)
125+
is File -> requestsWith.load(options.res as File)
126+
is Int -> requestsWith.load(options.res as Int)
127+
is ByteArray -> requestsWith.load(options.res as ByteArray)
128+
else -> requestsWith.load(options.res)
129+
}
130+
131+
glideRequest.apply {
132+
// 占位图、错误图
133+
...
134+
//缓存配置,优先级,缩略图请求
135+
...
136+
//动画、transformation
137+
into(GlideImageViewTarget(options.imageView, options.res))
138+
}
139+
140+
options.onProgressListener?.let {
141+
ProgressManager.addListener(options.res.toString(), options.onProgressListener)
66142
}
67-
onFail {
143+
}
144+
private fun glideRequests(context: Any?): GlideRequests {
145+
return when (context) {
146+
is Context -> IGlideModule.with(context)
147+
is Activity -> IGlideModule.with(context)
148+
is FragmentActivity -> IGlideModule.with(context)
149+
is Fragment -> IGlideModule.with(context)
150+
is android.app.Fragment -> IGlideModule.with(context)
151+
is View -> IGlideModule.with(context)
152+
else -> throw NullPointerException("not support")
68153
}
69154
}
70155
}
71-
//超长扩展函数 选用建议用上面DSL方式
72-
iv_9.loadImage(load = R.drawable.test, with = MainActivity@ this,
73-
placeHolderResId = R.color.black,errorResId = R.color.blue,isAnim = false,
74-
requestListener = object : OnImageListener {
75-
...
76-
},
77-
onProgressListener = object : OnProgressListener {
78-
...
79-
}, transformation = *arrayOf(GrayscaleTransformation())
80-
)
81156
```
82157

83-
## 可选扩展函数和Api介绍
158+
## Kotlin DSL
159+
160+
DSL的编写可以用下面代码简单理解和记忆(主要参考:[如何让你的回调更具Kotlin风味 (juejin.cn)](https://juejin.cn/post/6844903769436585991)
161+
162+
```kotlin
163+
class DSLTest{
164+
//普通变量
165+
var str:String? =null
166+
//函数变量
167+
var onSuccess: ((String?) -> Unit)? = null
168+
//调用函数变量
169+
fun onSuccessDo() {
170+
...
171+
onSuccess.invoke("success $str")
172+
}
173+
}
174+
//定义调用的函数
175+
load(dslPar:(DSLTest.() -> Unit)? = null){
176+
DSLTest().also(dslPar)
177+
}
178+
//使用
179+
load{
180+
str = "ACC"
181+
onSuccess{
182+
//TODO
183+
}
184+
}
185+
```
186+
187+
## 与传统策略模式封装对比
188+
189+
- 定义策略模式基础接口
190+
191+
```kotlin
192+
/** 图片加载策略 接口*/
193+
public interface BaseImageLoaderStrategy {
194+
void loadImage(load Any,ImageOptions options);
195+
}
196+
```
197+
198+
- 实现策略接口
199+
200+
```kotlin
201+
/*** 具体的加载策略, Glide 加载框架*/
202+
public class GlideImageLoader implements BaseImageLoaderStrategy {
203+
@Override
204+
public void loadImage(Context context, ImageOptions options) {
205+
Glide.with(context).apply(...).into(options.getImgView());
206+
}
207+
}
208+
```
209+
210+
- 策略调度器调用
211+
212+
```kotlin
213+
ImageLoader.loadImage(ImageOptions(imageView).apply{
214+
.....
215+
})
216+
```
217+
218+
策略模式一般会封装很多接口满足日常需求,由于kotlin特性我们封装一个超长参数的方法,然后使用可选参数的方式调用,但是java就无能为力了,只能对可选参数赋值null。
84219

85220
```kotlin
86-
ImageView.loadImage(...)
87-
ImageView.loadProgressImage(...)
88-
ImageView.loadResizeImage(...)
89-
ImageView.loadGrayImage(...)
90-
ImageView.loadBlurImage(...)
91-
ImageView.loadBlurImage(...)
92-
ImageView.loadRoundCornerImage(...)
93-
ImageView.loadCircleImage(...)
94-
ImageView.loadBorderImage(...)
95-
ImageView.load(load: Any?, options: ImageOptions.() -> Unit)//DSL
221+
//可选参数示例
222+
load(url:String,isCirle:Boolean = false, width:Int=0, height:Int = 0){
223+
.....
224+
}
225+
//用 参数名 = 值 使用可选参数
226+
iv_8.load(url2, height = 800)
96227
```
97228

229+
策略模式封装写法和扩展函数+DSL写法对比:
230+
231+
- 统一的接口封装,都具有可扩展性
232+
- 都可以用kotlin特性不用定义大量接口
233+
- 扩展函数更加方便简洁
234+
- DSL的写法让代码更加易懂,更具kotlin风格
235+
- 多方法接口回调,可以只选择个别方法
236+
237+
## 总结
238+
239+
- 最后要方便的使用到项目中那就打包发布jitpack仓库,项目开源地址和文档
240+
241+
[forJrking/ImageExt: 基于Glide封装ImageView加载图片资源的扩展函数集 (github.com)](https://github.com/forJrking/ImageExt)
242+
243+
- 由于使用到基于okhttp的下载进度管理所以使用了 glide 的@GlideModule配置方法,这样可能会和你项目自定义配置有冲突,目前只能拉代码自己修改,然后依赖Module方式了。如有更好方式联系我改进。
244+
245+
- Android图片加载库常见的只有几种,其他库可以自行参考实现。Kotlin真香!!!
246+
247+
248+
## Api介绍
249+
250+
98251
| `load: Any?` | 加载资源 |
99252
| ------------------------------------------------------------ | ------------------------------------------------------------ |
100253
| `with: Any?` | Glide.with( )参数,默认用ImageView |
@@ -123,25 +276,23 @@ ImageView.load(load: Any?, options: ImageOptions.() -> Unit)//DSL
123276
| `requestListener: OnImageListener?` | 加载结果监听,成功和失败 |
124277

125278

126-
127279
## CircleProgressView 仿微博图片加载
128-
129280
就是原封不动来自[GlideImageView](https://github.com/sunfusheng/GlideImageView) ,在布局中加入即可,有三种样式可供选择。
130281
```xml
131282
<CircleProgressView
132-
android:id="@+id/progressView"
133-
android:layout_width="50dp"
134-
android:layout_height="50dp"
135-
android:layout_centerInParent="true"
136-
android:layout_margin="10dp"
137-
android:progress="0"
138-
android:visibility="gone"
139-
app:cpv_progressNormalColor="@color/transparent10"
140-
app:cpv_progressReachColor="@color/transparent90_white"
141-
app:cpv_progressStyle="FillInnerArc"
142-
app:cpv_progressTextColor="@color/red"
143-
app:cpv_progressTextSize="13sp"
144-
app:cpv_progressTextVisible="false" />
283+
android:id="@+id/progressView"
284+
android:layout_width="50dp"
285+
android:layout_height="50dp"
286+
android:layout_centerInParent="true"
287+
android:layout_margin="10dp"
288+
android:progress="0"
289+
android:visibility="gone"
290+
app:cpv_progressNormalColor="@color/transparent10"
291+
app:cpv_progressReachColor="@color/transparent90_white"
292+
app:cpv_progressStyle="FillInnerArc"
293+
app:cpv_progressTextColor="@color/red"
294+
app:cpv_progressTextSize="13sp"
295+
app:cpv_progressTextVisible="false" />
145296
```
146297
## SelectImageView 仿微信图片点击响应
147298
一个点击可以变为半透明

0 commit comments

Comments
 (0)