-- By Denis.zheng
Glide 是 Google 官方推荐的一款图片加载库,使用起来也非常的简单便利,Glide 它帮我们完成了很多很重要,但是却通用的功能,例如:图片的加载压缩、展示、加载图片的内存管理等等。
笔者近期在图库项目中也大量使用了Glide来进行图片的加载展示和处理,关于Glide的常规用法,网络上有很多,在这里就不详细讲,本文主要讲下Gallery3.0中使用Glide时遇到的一些问题及注意事项。
注:本文使用的Glide版本为3.7,而最新的Glide4.0使用方法与3.x版本差别很大。
一. 图片质量
Gallery开发中,有QA提出了图片质量差的问题:
1.拍摄的照片异常,有一圈一圈的水印
2.圖庫照片顯示解析度壓縮過低
所提到的水印其实是摩尔纹,这是由于Glide图片质量默认使用RGB_565
安卓图片显示的质量配置主要分为四种:
ARGB_8888 :32位图,带透明度,每个像素占4个字节
ARGB_4444 :16位图,带透明度,每个像素占2个字节
RGB_565 :16位图,不带透明度,每个像素占2个字节
ALPHA_8 :32位图,只有透明度,不带颜色,每个像素占4个字节
(A代表透明度,RGB代表红绿蓝:即颜色)
而要解决上述问题,需要把Glide图片质量改为更高的ARGB_8888,我们通过自定义Glide实现:
1.创建一个类实现GlideModule:
1. /**
2. * Created by denis.zheng on 2017/9/27.
3. */
4.
5. public class GalleryGlideModule implementsGlideModule {
6. @Override
7. public void applyOptions(Context context,GlideBuilder builder) {
8.
9. }
10.
11. @Override
12. public void registerComponents(Context context,Glide glide) {
13.
14. }
15. }
2.配置清单文件:
在AndroidManifest中配置刚刚创建的GlideModule,需要在application节点下添加:
1. <meta-data
2. android:name="com.android.gallery3d.app.GalleryGlideModule"
3. android:value="GlideModule" />
其中Android:name就是刚才创建的GlideModule的实现类
3.进行自定义配置:
刚才创建的GlideModule的实现类时,会要实现两个方法,这里要用到的是其中的applyOptions方法,applyOptions方法里面提供了一个GlideBuilder,通过GlideBuilder我们就能实现自定义配置了
1. /**
2. * Created by denis.zheng on 2017/9/27.
3. */
4.
5. public class GalleryGlideModule implementsGlideModule {
6. @Override
7. public void applyOptions(Context context,GlideBuilder builder) {
8. //builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
9. builder.setDiskCache();//自定义磁盘缓存
10.
11. builder.setMemoryCache();//自定义内存缓存
12.
13. builder.setBitmapPool(); //自定义图片池
14.
15. builder.setDiskCacheService();//自定义本地缓存的线程池
16.
17. builder.setResizeService();//自定义核心处理的线程池
18.
19. builder.setDecodeFormat();//自定义图片质量
20. }
21.
22. @Override
23. public void registerComponents(Context context,Glide glide) {
24.
25. }
26. }
4.配置默认图片质量:
我们这边只讲如何将图片质量配置为ARGB_8888,如下:
1. /**
2. * Created by denis.zheng on 2017/9/27.
3. */
4.
5. public class GalleryGlideModule implementsGlideModule {
6. @Override
7. public void applyOptions(Context context, GlideBuilderbuilder) {
8. builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
9. }
10.
11. @Override
12. public void registerComponents(Context context,Glide glide) {
13.
14. }
15. }
二. 缓存策略
Glide使用缓存策略如下:
1. .diskCacheStrategy(DiskCacheStrategy.RESULT)
Glide 缓存了原始图像,全分辨率图像和另外小版本的图像,因此禁用磁盘缓存是用枚举来控制的
1. DiskCacheStrategy.NONE //什么都不缓存,就像刚讨论的那样
2. DiskCacheStrategy.SOURCE//仅仅只缓存原来的全分辨率的图像
3. DiskCacheStrategy.RESULT//仅仅缓存最终的图像,即,降低分辨率后的(或者是转换后的)
4. DiskCacheStrategy.ALL //缓存所有版本的图像(默认行为)
三.signature
缓存图片后,可能会遇到一种情况:图片路径不变,但图片内容发生了变化,在Gallery3.0人脸模块中,也遇到这种情况,由到不同的人脸封面可能在同一张图片中,所以早期开发时遇到这种情况,明明是不同人物的人脸相册,但是相册封面却是一样的。
所幸Glide中提供了signature()方法,将一个附加的数据加入到缓存key当中,多媒体存储数据,可用MediaStoreSignature类作为标识符,会将文件的修改时间、mimeType等信息作为cacheKey的一部分。
1. Glide.with(mContext)
2. .load(facePath)
3. .asBitmap()
4. .placeholder(R.drawable.photo_item_bg)//占位
5. .signature(newStringSignature(facePath+"_"+position.toString()))//传入path+postion做为cachekey
6. .diskCacheStrategy(DiskCacheStrategy.RESULT)
7. .dontAnimate()
8. .override(itemWidth/2,itemHeight/2)
9. .transform(newFaceTransformation(mContext,position,itemWidth,itemHeight,facePath))
10. .into(thumb);
如上,人脸数据中,每个相册封面图片的路径+人脸在图片上的方位是唯一的,所以我在这里传入图片路径+方位作为cachekey,这样就解决了上述问题。
四. 自定义变换Transformation
在加载图片中,有时我们不需要原图,需要对图片进行一些处理后再展示,比如载剪,圆角,旋转之类的,这时候就可以用上Glide提供的transform()方法,简单来说,transform()的作用就是改变原始资源在客户端上最终的展现结果。
Gallery中,使用到Transformation的模块主要是人脸相册封面的生成,因为人脸封面需要在根据人脸识别后的数据(主要是图片路径及这个脸在图片上的矩形位置),在图片中载剪出这个人的面孔并展示出来。同时连拍优选中的圆角图片展示,及图片浏览界面的图片旋转处理都使用到Transformation。
Transformation使用有两种方法,最简单的方式就是直接继承BitmapTransformation:
1. /**
2. * Created by denis.zheng on 2017/8/18.
3. */
4.
5. public class RotateTransformation extends BitmapTransformation {
6. private floatrotateRotationAngle = 0f;
7.
8. public RotateTransformation(Context context, floatrotateRotationAngle) {
9. super( context );
10. this.rotateRotationAngle =rotateRotationAngle;
11. }
12. @Override
13. protected Bitmap transform(BitmapPool pool,Bitmap bitmap, int i, int i1) {
14. int exifOrientationDegrees =getExifOrientationDegrees((int)rotateRotationAngle);
15. return TransformationUtils.rotateImageExif(bitmap,pool, exifOrientationDegrees);
16. }
17.
18. @Override
19. public String getId() {
20. return "rotate" +rotateRotationAngle;
21. }
22. }
使用方法:
1. Glide
2. .with(this)
3. .load(item.getPath())
4. .asBitmap()
5. .fitCenter()
6. .placeholder(((PhotoView)currView).getDrawable())
7. .dontAnimate()
8. .transform(newRotateTransformation(this, 0))
9. .override(display.widthPixels, display.heightPixels)
10. .into((PhotoView)currView);
也可以直接实现Transformation接口,进行更灵活的图片处理,如连拍优选中的圆角处理:
1. public class BurstCircleTransform implementsTransformation<Bitmap> {
2. private float mRadius;
3. private String mPath;
4. private BitmapPool mBitmapPool;
5. public BurstCircleTransform(Contextcontext,String path, float radius) {
6. this(Glide.get(context).getBitmapPool(),path,radius);
7. }
8.
9. public BurstCircleTransform(BitmapPoolmBitmapPool,String path, float radius){
10. this.mRadius=radius;
11. this.mPath = path;
12. this.mBitmapPool=mBitmapPool;
13. }
14. @Override
15. public Resource<Bitmap> transform(Resource<Bitmap>resource,int outWidth, int outHeight) {
16. Bitmap source = resource.get();
17. int width = source.getWidth();
18. int height = source.getHeight();
19. Bitmap result = mBitmapPool.get(width, height,Bitmap.Config.ARGB_8888);
20. if (result == null) {
21. result = Bitmap.createBitmap(width, height,Bitmap.Config.ARGB_8888);
22. }
23. Canvas canvas = new Canvas(result);
24. Paint paint = new Paint();
25. paint.setAntiAlias(true);
26. paint.setShader(newBitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
27. canvas.drawRoundRect(new RectF(0, 0, width, height),mRadius, mRadius, paint);
28. return BitmapResource.obtain(result, mBitmapPool);
29. }
30.
31. @Override
32. public String getId() {
33. return mPath+"_"+mRadius;
34. }
35. }
在使用自定义变换中,需要注意几点:
自定义图片处理时Glide会自动计算View/Target大小,我们不需要传View的宽高,当然你可以使用override(int, int)去改变这种行为。
自定义图片处理时,为了避免创建大量Bitmap以及减少GC,可以考虑重用Bitmap,这就需要BitmapPool,典型地就是,从Bitmap池中拿一个Bitmap,用这个Bitmap生成一个Canvas, 然后在这个Canvas上画初始的Bitmap并使用Matrix、Paint、或者Shader处理这张图片。为了有效并正确重用Bitmap需要遵循以下三条准则:
1.永远不要把transform()传给你的原始resource或原始Bitmap给recycle()了,更不要放回BitmapPool,因为这些都自动完成了。值得注意的是,任何从BitmapPool取出的用于自定义图片变换的辅助Bitmap,如果不经过transform()方法返回,就必须主动放回BitmapPool或者调用recycle()回收。
2.如果你从BitmapPool拿出多个Bitmap或不使用你从BitmapPool拿出的一个Bitmap,一定要返回extras给BitmapPool。
3.如果你的图片处理没有替换原始resource,就返回原始resource或原始Bitmap。
1. protected Bitmap transform(BitmapPool pool,Bitmap toTransform, int outWidth, int outHeight) {
2. Bitmap result = pool.get(outWidth,outHeight, Bitmap.Config.ARGB_8888);
3. // 如果BitmapPool中找不到符合该条件的Bitmap,get()方法会返回null,就需要我们自己创建Bitmap了
4. if (result == null) {
5. // 如果想让Bitmap支持透明度,就需要使用ARGB_8888
6. result = Bitmap.createBitmap(outWidth,outHeight, Bitmap.Config.ARGB_8888);
7. }
8. //创建最终Bitmap的Canvas.
9. Canvas canvas = new Canvas(result);
10. Paint paint = new Paint();
11. paint.setAlpha(128);
12. // 将原始Bitmap处理后画到最终Bitmap中
13. canvas.drawBitmap(toTransform,0, 0, paint);
14. // 由于我们的图片处理替换了原始Bitmap,就return我们新的Bitmap就行。
15. // Glide会自动帮我们回收原始Bitmap。
16. return result;
17. }
关于这部分图片处理推荐使用第三方库 https://github.com/wasabeef/glide-transformations
五. 总结
本文主要是总结了在开发Gallery中使用Glide,遇到的一些注意事项,其实Glide的使用还有不少内容,网络上有很多,大家可以自行查找。