图片加载框架之Glide和Picasso
Glide介绍
Glide是一个加载图片的库,作者是bumptech,它是在泰国举行的google 开发者论坛上google为我们介绍的,这个库被广泛的运用在google的开源项目中。
Glide是一个非常成熟的图片加载库,他可以从多个源加载图片,如:网路,本地,Uri等,更重要的是他内部封装了非常好的缓存机制并且在处理图片的时候能保持一个低的内存消耗。
Picasso介绍(毕加索)
picasso是Square公司开源的一个Android图形缓存库,地址http://square.github.io/picasso/,可以实现图片下载和缓存功能。仅仅只需要一行代码就能完全实现图片的异步加载
1.在adapter中需要取消已经不在视野范围的ImageView图片资源的加载,否则会导致图片错位,Picasso已经解决了这个问题。
2.使用复杂的图片压缩转换来尽可能的减少内存消耗
3.自带内存和硬盘二级缓存功能
二者用法类似,根据gihthub上添加依赖库,添加联网权限
区别:
区别一:with的参数
将Activity/Fragment作为with参数的好处:图片加载和Activity/Fragment的生命周期保持一致,比如Pause状态暂停加载,在resume时候重新加载,建议传参的时候给Activity/Fragment给Glide而不是context
区别二:图片质量
Glide默认的bitmap格式为RGB_565
Picasso默认的bitmap格式为ARGB_8888
区别三:加载Gif图片
Glide的一个明显的优点就是它可以加载gif图片,用Picasso加载的gif图片是不会动的
因为Glide被设计成能和Activity/Fragment的生命周期完美的相结合,因此gif动画将随着Activity/Fragment的生命周期自动的开始和停止。
gif的缓存和一般的图片也是一样的,也是第一次加载的时候调整大小,然后缓存。
注意:gif图片将消耗非常多的内存,因此要慎用。
区别四:缓存策略和加载速度
Picasso的缓存是全尺寸的,而Glide的缓存根据ImageView的尺寸相同
将ImageView调整成不同的大小,不管大小如何,Picasso值缓存一个全尺寸的,Picasso则需要在显示前重新调整大小而导致一下延迟.
而Glide不同,它会为每种大小尺寸缓存一下,加载速度比Picasso更快,磁盘策略比Picasso更好,但需要更大的空间来缓存,Glide比Picasso更有利于减少OOM的发生
基本用法
Glide.with(this).load(url).into(imageView); Picasso.with(this).load(url).into(imageView);
Glide:修改缓存大小、位置、加载图片质量的实现
自定义一个glideConfigModule.java实体类继承GlideModule
import android.content.Context; import com.bumptech.glide.Glide; import com.bumptech.glide.GlideBuilder; import com.bumptech.glide.load.DecodeFormat; import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool; import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory; import com.bumptech.glide.load.engine.cache.LruResourceCache; import com.bumptech.glide.module.GlideModule; public class GlideConfigModule implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { // 指定位置在packageName/cache/glide_cache,大小为MAX_CACHE_DISK_SIZE的磁盘缓存 builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "glide_cache", 10*1024*1024)); //指定内存缓存大小 Runtime.getRuntime().maxMemory()运行的最大内存值 builder.setMemoryCache(new LruResourceCache(10*1024*1024)); //全部的内存缓存用来作为图片缓存 builder.setBitmapPool(new LruBitmapPool(10*1024*1024)); //设置图片质量格式,设置和Picasso配置一样 builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);/ } @Override public void registerComponents(Context context, Glide glide) { } }
在清单文件中配置
<meta-data android:name="com.jcf.glidedemo.GlideConfigModule" android:value="GlideModule"/>
Glide:不为ImageView类型加载的实现
譬如加载的控件类型不是ImageView,是个自定义的布局。或者加载为Background的形式。可以使用SimpleTarget
类型,这里指定他的大小为500*100,加载为背景图片。
.into(new SimpleTarget<GlideDrawable>(500,200) { @Override public void onResourceReady(GlideDrawable glideDrawable, GlideAnimation<? super GlideDrawable> glideAnimation) { tv.setBackground(glideDrawable); } });
Picasso自定义截取图片的实现
import android.graphics.Bitmap; import com.squareup.picasso.Transformation; public class CropSquareTransformation implements Transformation { @Override public Bitmap transform(Bitmap bitmap) { int size = Math.min(bitmap.getWidth(), bitmap.getHeight()); int x = (bitmap.getWidth() - size) / 2; int y = (bitmap.getHeight() - size) / 2; Bitmap result = Bitmap.createBitmap(bitmap, x, y, size, size); if (result != bitmap) { bitmap.recycle(); } return result; } @Override public String key() { return "square()"; } }
使用
Picasso.with(this) .load(url) .transform(new CropSquareTransformation()) .into(ivPicasso);
二者在开发中用法
Glide.with(this) .load("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1483868451&di=2c4c2a41347e49eca30ee3a2276034c0&src=http://file27.mafengwo.net/M00/B2/12/wKgB6lO0ahWAMhL8AAV1yBFJDJw20.jpeg") .crossFade() .listener(mRequestListener)//配置监听器 .animate(android.R.anim.slide_in_left) //配置自带淡出淡入动画 //.animate(R.anim.scale) //可设置自定义的动画 .override(100, 100) //为图片重新定义大小 .placeholder(R.mipmap.ic_launcher)//加载中的图片 //.fitCenter() //根据布局大小填充图片,必须和centerCrop一起设置设置fitCenter(),不能再调用override() //.centerCrop() //图片要填充整个控件,去两边留中间 .error(R.mipmap.ic_launcher)//加载失败的涂料 .priority(Priority.HIGH) //优先级 .into(ivGlide);//加载到控件上 Picasso.with(this) // .load("https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1483868451&di=2c4c2a41347e49eca30ee3a2276034c0&src=http://file27.mafengwo.net/M00/B2/12/wKgB6lO0ahWAMhL8AAV1yBFJDJw20.jpeg") .load("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1483885293073&di=4d6626505c2c3ca8a9e7f580c08884a8&imgtype=0&src=http%3A%2F%2Fimg5.duitang.com%2Fuploads%2Fitem%2F201510%2F30%2F20151030165654_fyUJW.thumb.700_0.gif") .error(R.mipmap.ic_launcher) //.fit() //控件不能设置成wrap_content,也就是必须有大小才行,fit()才让图片的宽高等于控件的宽高,设置fit(),不能再调用resize() //.centerCrop() //图片要填充整个控件,去两边留中间 .resize(100,100) ////为图片重新定义大小 .placeholder(R.mipmap.ic_launcher) .priority(Picasso.Priority.HIGH) .into(ivPicasso); } private RequestListener<String, GlideDrawable> mRequestListener = new RequestListener<String, GlideDrawable>() { @Override public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) { //显示错误信息 Log.w(TAG, "onException: ", e); //打印请求URL Log.d(TAG, "onException: " + model); //打印请求是否还在进行 Log.d(TAG, "onException: " + target.getRequest().isRunning()); return false; } @Override public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { if (isFromMemoryCache) { //如果是从缓存加载,设置动画效果 ivGlide.setAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.scale)); } //返回true表示拦截不再传递,false表示事件会传递下去 return false; } };
Picasso圆形图片的实现
// 自定义Transformation Transformation transform = new Transformation() { @Override public Bitmap transform(Bitmap source) { int size = Math.min(source.getWidth(), source.getHeight()); int x = (source.getWidth() - size) / 2; int y = (source.getHeight() - size) / 2; Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size); if (squaredBitmap != source) { source.recycle(); } Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig()); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP); paint.setShader(shader); paint.setAntiAlias(true); float r = size / 2f; canvas.drawCircle(r, r, r, paint); squaredBitmap.recycle(); return bitmap; } @Override public String key() { return "circle"; } };
Picasso .with(this)// 指定Context .load(URL_IMG2) //指定图片URL .transform(transform) // 指定图片转换器 .into(imageView); // 指定显示图片的ImageView
Picasso圆形图片的实现 class RoundedTransformation implements com.squareup.picasso.Transformation { private final int radius; private final int margin; // dp // radius is corner radii in dp // margin is the board in dp public RoundedTransformation(final int radius, final int margin) { this.radius = radius; this.margin = margin; } @Override public Bitmap transform(final Bitmap source) { final Paint paint = new Paint(); paint.setAntiAlias(true); paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint); if (source != output) { source.recycle(); } return output; } @Override public String key() { return "rounded(radius=" + radius + ", margin=" + margin + ")"; } }
Picasso .with(this)// 指定Context .load(URL_IMG2) //指定图片URL .transform(new RoundedTransformation(360,0)) // 指定图片转换器 .into(imageView); // 指定显示图片的ImageView
Glide显示圆形图片的实现
class GlideCircleTransform extends BitmapTransformation { public GlideCircleTransform(Context context) { super(context); } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { return circleCrop(pool, toTransform); } private Bitmap circleCrop(BitmapPool pool, Bitmap source) { if (source == null) return null; int size = Math.min(source.getWidth(), source.getHeight()); int x = (source.getWidth() - size) / 2; int y = (source.getHeight() - size) / 2; // TODO this could be acquired from the pool too Bitmap squared = Bitmap.createBitmap(source, x, y, size, size); Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888); if (result == null) { result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(result); Paint paint = new Paint(); paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); paint.setAntiAlias(true); float r = size / 2f; canvas.drawCircle(r, r, r, paint); return result; } @Override public String getId() { return getClass().getName(); } } Glide .with(this) // 指定Context .load(URL_GIF)// 指定图片的URL .transform(new GlideCircleTransform(this)) // 指定自定义图片样式 .into(imageView);//指定显示图片的ImageView
Glide显示圆角图片的实现
class GlideRoundTransform extends BitmapTransformation { private float radius = 0f; public GlideRoundTransform(Context context) { this(context, 4); } public GlideRoundTransform(Context context, int dp) { super(context); this.radius = Resources.getSystem().getDisplayMetrics().density * dp; } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { return roundCrop(pool, toTransform); } private Bitmap roundCrop(BitmapPool pool, Bitmap source) { if (source == null) return null; Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); if (result == null) { result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(result); Paint paint = new Paint(); paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); paint.setAntiAlias(true); RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight()); canvas.drawRoundRect(rectF, radius, radius, paint); return result; } @Override public String getId() { return getClass().getName() + Math.round(radius); } }
Glide .with(this) // 指定Context .load(URL_GIF)// 指定图片的URL .transform(new GlideRoundTransform(this,30)) // 指定自定义图片样式 .into(imageView);//指定显示图片的ImageView