Android平滑图片加载和缓存库 Glide 使用详解

一、简介

在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫Glide的图片加载库,作者是bumptech。这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会上发布的官方app。

二、 使用

dependencies {

compile 'com.github.bumptech.glide:glide:3.6.0'

compile 'com.android.support:support-v4:23.3.0’

}

如何查看最新版本

http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22glide%22

三 、使用方法及详细介绍

http://mrfu.me/2016/02/27/Glide_Getting_Started/介绍的很详细,强烈建议看看。

Yelp app是如何使用Glide优化图片加载的

原文:Glide – How Yelp’s Android App Loads Images

动态加载图片是很多安卓应用的基础。在Yelp(美国最大点评网站)中,图片在把消费者与商家联系起来的过程中至关重要。随着网络通信和硬件水平的越发强大,消费者对于图片数量和图片质量的期望日益增长。图片可以轻易的成为内存和网络流量的消耗大户,处理图片数据的下载和管理成为了一个让人望而却步的任务。我们探索了几种处理这个问题的解决办法,最终认为Glide在性能,使用方便性,稳定性上达到了相当好的平衡。

Glide最简单的使用案例就是从远程服务器或者本地文件系统加载图片,把它们放在磁盘与内存缓存中,然后加载到view上。它可以用在全市图片的app中,Glide为包含图片的滚动列表做了尽可能流畅的优化。

对象池

Glide原理的核心是为bitmap维护一个对象池。对象池的主要目的是通过减少大对象的分配以重用来提高性能(至于对象池的概览,可以查看这个Android performance pattern视频)。

Dalvik和ART虚拟机都没有使用compacting garbage collector,compacting garbage collector是一种模式,这种模式中GC会遍历堆,同时把活跃对象移到相邻内存区域,让更大的内存块可以用在后续的分配中。因为安卓没有这种模式,就可能会出现被分配的对象分散在各处,对象之间只有很小的内存可用。如果应用试图分配一个大于邻近的闲置内存块空间的对象,就会导致OutOfMemoryError,然后崩溃,即使总的空余内存空间大于对象的大小。

使用对象池还可以帮助提高滚动的性能,因为重用bitmap意味着更少的对象被创建与回收。垃圾回收会导致“停止一切(Stop The World)”事件,这个事件指的是回收器执行期间,所有线程(包括UI线程)都会暂停。这个时候,图像帧无法被渲染同时UI可能会停滞,这在滚动期间尤其明显。

scrolling-slight

Glide的使用

Glide使用起来很简单,而且不需要任何特别的配置就自动包含了bitmap pooling。

DrawableRequestBuilder requestBuilder = Glide.with(context).load(imageUrl);

requestBuilder.into(imageView);

这就是加载一张图片的全部要求。就像安卓中的很多地方一样,with()方法中的context到底是哪种类型是不清楚的。有一点很重要需要记住,就是传入的context类型影响到Glide加载图片的优化程度,Glide可以监视activity的生命周期,在activity销毁的时候自动取消等待中的请求。但是如果你使用Application context,你就失去了这种优化效果。

译者注:其实以上的代码是一种比较规范的写法,我们更熟悉的写法是:

Glide.with(context)

.load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")

.into(ivImg);

优化特性

类似的是,如果相关的item已经滚出了屏幕的范围,Glide会自动取消列表中的悬着的图片请求。因为绝大多数开发者都会在adapter中利用view的回收,Glide做到这点是通过在ImageView上设置一个tag,在加载另外一张图片之前检查这个tag,如果存在就取消第一次请求。

Glide提供了几个让你感觉图片加载速度变快的特性。第一个就是在图片显示在屏幕上之前就预先取出图片。它提供了一个ListPreloader类,它被应该事先取出的item数目实例化。然后通过setOnScrollListener(OnScrollListener).被传递给ListView。你想在ListView之外也能预先取出图片吗?没问题,使用前面的builder对象就可以了,只需调用builder.downloadOnly()。

downloadOnly见:https://github.com/bumptech/glide/wiki/Loading-and-Caching-on-Background-Threads。

我们发现了Glide提供的可以大大提高性能,稳定性的功能,以及安卓图片加载领域的一些设计哲学。这些特性和优化确实可以很好的将图片加载的体验变成一种享受。

Glide:快速和高效的Android平台多媒体资源管理库

Glide是一个android平台上的快速和高效的开源的多媒体资源管理库,提供多媒体文件的压缩,内存和磁盘缓存,资源池的接口

glide_logo

Glide支持获取,解压展示视频,图像和GIFs,  Glide有一个可弹性的api可以让开发者自定义网络栈技术,默认使用HttpUrlConnection ,你可以替换为Google’s Volley或者OkHttp

Glide开始的目的是提供一个快速和平滑展示图片列表,但是Glide对你需要拉取, resize和展示远程的图片这些场景都是很管用的.

使用

参考这里GitHub wiki和这里javadocs.

简单的例子

// For a simple view:

@Override

public void onCreate(Bundle savedInstanceState) {

...

ImageView imageView = (ImageView) findViewById(R.id.my_image_view);

Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);

}

// For a list:

@Override

public View getView(int position, View recycled, ViewGroup container) {

final ImageView myImageView;

if (recycled == null) {

myImageView = (ImageView) inflater.inflate(R.layout.my_image_view,

container, false);

} else {

myImageView = (ImageView) recycled;

}

String url = myUrls.get(position);

Glide.with(myFragment)

.load(url)

.centerCrop()

.placeholder(R.drawable.loading_spinner)

.crossFade()

.into(myImageView);

return myImageView;

}

Volley

如果你想使用Volley

Gradle

dependencies{

compile'com.github.bumptech.glide:volley-integration:1.0.+'

compile'com.mcxiaoke.volley:library:1.0.+'

}

然后在你的Activity或者程序中,注册Volley为基本模块

public void onCreate() {

Glide.get(this).register(GlideUrl.class, InputStream.class,

new VolleyUrlLoader.Factory(yourRequestQueue));

...

}

OkHttp

Gradle:

dependencies{

compile'com.github.bumptech.glide:okhttp-integration:1.0.+'

compile'com.squareup.okhttp:okhttp:2.0.+'

}

然后在你的Activity或者程序中,注册OkHttp为基本模块

public void onCreate() {

Glide.get(this).register(GlideUrl.class, InputStream.class,

new OkHttpUrlLoader.Factory(yourOkHttpClient));

...

}

四、图片的缓存和缓存的时效机制

1.图片缓存的键值

图片缓存的键值主要用于DiskCacheStrategy.RESULT,Glide当中的键值主要包含三个部分:

通过DataFetcher.getId()方法返回的String数据作为键值。一般的DataFetchers会简单返回数据模型data model的toString()结果,如果是URL/File会返回相应的路径

图片的尺寸,主要是通过override(width,height)或者通过Target's getSize()方法确定的尺寸信息

包含一个可选的签名所有的这些东西会通过一种散列算法生成一个独有、安全的文件名,通过此文件名将图片缓存在disk中

2.缓存失效

因为Glide当中图片缓存key的生成是通过一个散列算法来实现的,所以很难手动去确定哪些文件可以从缓存当中进行删除

2.1 当内容(url,file path)改变的时候,改变相应的标识符就可以了,Glide当中也提供了signature()方法,将一个附加的数据加入到缓存key当中

多媒体存储数据,可用MediaStoreSignature类作为标识符,会将文件的修改时间、mimeType等信息作为cacheKey的一部分

文件,使用StringSignature

Urls ,使用StringSignature

Glide.with(yourFragment)

.load(yourFileDataModel)

.signature(new StringSignature(yourVersionMetadata))

.into(yourImageView);

Glide.with(fragment)

.load(mediaStoreUri)

.signature(new MediaStoreSignature(mimeType, dateModified, orientation))

.into(view);

自定义标识符:

public class IntegerVersionSignature implements Key {

private int currentVersion;

public IntegerVersionSignature(int currentVersion) {

this.currentVersion = currentVersion;

}

@Override

public boolean equals(Object o) {

if (o instanceof IntegerVersionSignature) {

IntegerVersionSignature other = (IntegerVersionSignature) o;

return currentVersion = other.currentVersion;

}

return false;

}

@Override

public int hashCode() {

return currentVersion;

}

@Override

public void updateDiskCacheKey(MessageDigest md) {

messageDigest.update(ByteBuffer.allocate(Integer.SIZE)

.putInt(signature).array());

}

}

2.2、不缓存可以通过diskCacheStrategy(DiskCacheStrategy.NONE.)实现

五、配置GlideModules

可以通过GlideModule接口来配置Glide的配置文件,并且像ModelLoaders一样注册相关组件。

包含一个GlideMode :

第一步、To use and register a GlideModule, first implement the interface with your configuration and components:

public class MyGlideModule implements GlideModule {

@Override

public void applyOptions(Context context, GlideBuilder builder) {

// Apply options to the builder here.

}

@Override

public void registerComponents(Context context, Glide glide) {

// register ModelLoaders here.

}

}

第二步、然后将上面的实现了加入到proguard.cfg当中:

-keepnames class * com.mypackage.MyGlideModule

第三步、在AndroidManifest.xml文件中添加meta-data,以便Glide能够找到你的Module

android:name="com.bumptech.glide.samples.flickr.FlickrGlideModule"

android:value="GlideModule" />

六、Library项目

一个Library项目可能会定义一个或者多个GlideModules,如果一个Library项目添加一个Module到Library项目的manifest当中,依赖于此Library的应用就会自动加载依赖库(Library项目)当中的Module。

当然,如果manifest的合并不正确,那么Library里面Module就必须手动地在应用当中添加进去。

七、GlideModules冲突

虽然Glide允许一个应用当中存在多个GlideModules,Glide并不会按照一个特殊的顺序去调用已注册的GlideModules,如果一个应用的多个依赖工程当中有多个相同的Modules,就有可能会产生冲突。

如果一个冲突是不可避免的,应用应该默认去定义一个自己的Module,用来手动地处理这个冲突,在进行Manifest合并的时候,可以用下面的标签排除冲突的module。

八、通过GlideBuilder配置全局配置文件

Glide允许开发者配置自定义的全局操作应用于所有的请求,这个部分可以通过GlideModule接口中的applyOptions方法的GlideBuilder参数实现 :

1.DiskCache

1.1、硬盘缓存是在一个后台线程当中,通过一个DiskCache.Factory接口进行缓存的。

开发者能够通过GlideBuilder的setDiskCache(DiskCache.Factory df)方法设置存储的位置和大小

通过传入DiskCacheAdapter来完全禁用缓存

自定义一个DiskCache来完全禁用缓存,

Glide默认是用InternalCacheDiskCacheFactory类来创建硬盘缓存的,这个类会在应用的内部缓存目录下面创建一个最大容量250MB的缓存文件夹,使用这个缓存目录而不用sd卡,意味着除了本应用之外,其他应用是不能访问缓存的图片文件的。

1.2.设置disk缓存的大小 : InternalCacheDiskCacheFactory

builder.setDiskCache(new InternalCacheDiskCacheFactory(context, yourSizeInBytes));

1.3.设置缓存的路径

可以通过实现DiskCache.Factory,然后使用DiskLruCacheWrapper创建一个新的缓存目录,比如,可以通过如下方式在外存当中创建缓存目录:

builder .setDiskCache(new DiskCache.Factory() {

@Override

public DiskCache build() {

// Careful: the external cache directory doesn't enforce permissions

File cacheLocation = new File(context.getExternalCacheDir(), "cache_dir_name");

cacheLocation.mkdirs();

return DiskLruCacheWrapper.get(cacheLocation, yourSizeInBytes);

}

});

2.内存当中的缓存和POOLS

GlideBuilder当中,允许开发者去设置内存当中图片缓存区的大小,主要涉及到的类包括MemoryCache和BitmapPool

2.1 大小的设置

默认内存缓存的大小是用过MemorySizeCalculator来实现的,这个类会根据设备屏幕的大小,计算出一个合适的size,开发者可以获取到相关的默认设置信息:

MemorySizeCalculator calculator = new MemorySizeCalculator(context);

int defaultMemoryCacheSize = calculator.getMemoryCacheSize();

int defaultBitmapPoolSize = calculator.getBitmapPoolSize();

如果在应用当中想要调整内存缓存的大小,开发者可以通过如下方式:

Glide.get(context).setMemoryCategory(MemoryCategory.HIGH);

2.2 Memory Cache

Glide内存缓存的目的是减少I/O,提高效率

可以通过GlideBuidler的setMemoryCache(MemoryCache memoryCache)去设置缓存的大小,开发者可以通过LruResourceCache类去设置缓存区的大小

builder.setMemoryCache(new LruResourceCache(yourSizeInBytes));

2.3 Bitmap Pool

可以通过GlideBuilder的setBitmapPool()方法设置池子的大小,LruBitmapPool是Glide的默认实现,使用如下:

builder.setBitmapPool(new LruBitmapPool(sizeInBytes));

2.4 图片格式

GlideBuilder允许开发者设置一个全局的默认图片格式,

在默认情况下,Glide使用RGB_565格式加载图片,如果想要使用高质量的图片,可以通过如下方式设置系统的图片格式:

builder.setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888);

九、自定义显示控件

除了可以将图片、视频快照和GIFS显示在View上面之外,开发者也可以在自定义的Target上面显示这些媒体文件

1.SimpleTarget

重点内容

如果你想简单地加载一个Bitmap,你可以通过以下简单的方式而不是直接地显示给用户,可能是显示一个notification,或者上传一个头像,Glide都能很好地实现

SimpleTarget提供了对Target的简单实现,并且让你专注于对加载结果的处理

为了使用SimpleTarget,开发者需要提供一个宽和高的像素值,用来加载你的资源文件,并且你需要去实现

onResourceReady(R resource,GlideAnimation glideAnimation)

int myWidth = 512;

int myHeight = 384;

Glide.with(yourApplicationContext))

.load(youUrl)

.asBitmap()

.into(new SimpleTarget(myWidth, myHeight) {

@Override

public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {

// Do something with bitmap here.

}

};

说明:

通常你去加载资源的时候,是将他们加载到一个view当中,当fragment或者activity失去焦点或者distroyed的时候,Glide会自动停止加载相关资源,确保资源不会被浪费

在大多数SimpleTarget的实现当中,如果需要资源的加载不受组件生命周期的影响,Glide.width(context)当中的context是application context而不是fragment或者activity

另外,由于一些long running operations可能会导致内存泄露,如果你打算使用一个这样的操作,可以考虑使用一个静态的内部类而不是一个动态的内部类。

2.ViewTarget

如果你想加载一张图片到一个view当中,但是又想改变或者监听Glide默认的部分设置,就可以通过重写ViewTarget或者他的子类来实现

如果你想Gidle加载图片的时候可以自定义图片的大小,或者想要设置一个自定义的显示动画,就可以通过ViewTarget来实现,可以通过一个静态的ViewTarget或者动态的内部类来实现相关的功能

Glide.with(yourFragment)

.load(yourUrl)

.into(new ViewTarget(yourViewObject) {

@Override

public void onResourceReady(GlideDrawable resource, GlideAnimation anim) {

YourViewClass myView = this.view;

// Set your resource on myView and/or start your animation here.

}

});

说明:

加载一张静态的图片或者一张GIF动态图,可以在load后面加上asBitmap()/asGif()

.Load(url)会通过asXXX()替换ViewTarget当中的GlideDrawable参数,也可以通过实现LifecycleLisener,给target设置一个回调。

3.覆盖默认的相关设置

如果只是想使用Glide的默认配置,可以使用Glide当中ImageViewTargets的两个子类:

GlideDrawableImageViewTarget 默认的实现,可以通过asGif()加载动态图片

BitmapImageViewTarget 可以通过asBitmap()加载静态图片

如果想要使用Glide默认实现,可以在他们的子类方法当中使用super.xx()即可,例如:

Glide.with(yourFragment)

.load(yourUrl)

.asBitmap()

.into(new BitmapImageViewTarget(yourImageView)) {

@Override

public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {

super.onResourceReady(bitmap, anim);

Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {

@Override

public void onGenerated(Palette palette) {

// Here's your generated palette

}

});

}

});

十、使用Glide下载自定义尺寸的图片

Glide的ModelLoader接口为开发者提供了装载图片的view的尺寸,并且允许开发者使用这些尺寸信息去选择合适的URL去下载图片。选用适当的尺寸可以节省宽带和设备的空间开销,提高app的性能

2014年googleI/o大会发表了一篇文章,阐述了他们如何使用ModelLoader接口去适配图片的尺寸,见下面的连接:https://github.com/google/iosched/blob/master/doc/IMAGES.md

1、通过http/https下载图片,可以通过继承BaseGlideUtlLoader来实现:

public interface MyDataModel {

public String buildUrl(int width, int height);

}

public class MyUrlLoader extends BaseGlideUrlLoader {

@Override

protected String getUrl(MyDataModel model, int width, int height) {

// Construct the url for the correct size here.

return model.buildUrl(width, height);

}

}

2、可以使用你自定义的ModelLoader去加载图片了

Glide.with(yourFragment)

.using(new MyUrlLoader())

.load(yourModel)

.into(yourView);

如果你想避免每次加载图片都要使用.using(new MyUrlLoader()) ,可以实现是一个ModelLoaderFactory然后使用Glide将它注册到GlideModule当中

public class MyGlideModule implements GlideModule {

...

@Override

public void registerComponents(Context context, Glide glide) {

glide.register(MyDataModel.class, InputStream.class,

new MyUrlLoader.Factory());

}

}

这样你就可以跳过.using()了

Glide.with(yourFragment)

.load(yourModel)

.into(yourView);

十一、在后台线程当中进行加载和缓存

为了保证Glide在后台线程当中加载资源文件更加容易,Glide除了Glide.with(fragment).load(url).into(view)之外还提供了

downloadOnly(int width, int height)

downloadOnly(Y target)// Y extends Target

into(int width, int height)

1.downloadOnly

Glide的downloadOnly()允许开发者将Image的二进制文件下载到硬盘缓存当中,以便在后续使用,

在UI线程当中异步下载,在异步线程当中则是使用width和height

在异步线程当中同步调用下载,在同步线程当中,

downloadOnly使用一个target作为参数

(1)在后台线程当中下载图片,可以通过如下的方式:

FutureTarget future = Glide.with(applicationContext)

.load(yourUrl)

.downloadOnly(500, 500);

File cacheFile = future.get();

当future返回的时候,image的二进制文件信息就存入了disk缓存了,值得注意的是downloadOnly API只是保证图片个bytes数据在disk当中是有效的。

(2)下载完毕之后如果想要进行显示,可以通过如下方式进行调用:

Glide.with(yourFragment)

.load(yourUrl)

.diskCacheStrategy(DiskCacheStrategy.ALL)

.into(yourView);

通过DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE,可以保证程序会去读取缓存文件

2. 如果想要在后台线程当中获取某个URL对应的Bitmap

不通过downloadOnly,可以使用into(),会返回一个FutureTarget对象,比如,想要得到一个URL对应的500*500的centerCrop裁剪图片,可以通过如下方式实现:

Bitmap myBitmap = Glide.with(applicationContext)

.load(yourUrl)

.asBitmap()

.centerCrop()

.into(500, 500)

.get()

注意:上面的调用只能在异步线程当中,如果在main Thread当中调用.get(),会阻塞主线

十二、转换器

1.默认的转换器

Glide两个默认的转换器,fitCenter和CenterCrop,其他的转换器详见https://github.com/wasabeef/glide-transformations,可以将图片转为各种形状,例如圆形,圆角型等等

用法

Glide.with(yourFragment)

.load(yourUrl)

.fitCenter()

.into(yourView);

Glide.with(yourFragment)

.load(yourUrl)

.centerCrop()

.into(yourView);

// For Bitmaps:

Glide.with(yourFragment)

.load(yourUrl)

.asBitmap()

.centerCrop()

.into(yourView);

// For gifs:

Glide.with(yourFragment)

.load(yourUrl)

.asGif()

.fitCenter()

.into(yourView);

甚至可以在两幅图片进行类型转换的时候进行transformed

Glide.with(yourFragment)

.load(yourUrl)

.asBitmap()

.toBytes()

.centerCrop()

.into(new SimpleTarget(...) { ... });

2.自定义转换器

方法:

第一步、编写转换器类 ,继承BitmapTransformation:

private static class MyTransformation extends BitmapTransformation {

public MyTransformation(Context context) {

super(context);

}

@Override

protected Bitmap transform(BitmapPool pool, Bitmap toTransform,

int outWidth, int outHeight) {

Bitmap myTransformedBitmap = ... // apply some transformation here.

return myTransformedBitmap;

}

@Override

public String getId() {

// Return some id that uniquely identifies your transformation.

return "com.example.myapp.MyTransformation";

}

}

第二步、在Glide方法链当中用.transform(…)替换fitCenter()/centerCrop()

Glide.with(yourFragment)

.load(yourUrl)

.transform(new MyTransformation(context))

.into(yourView);

// For Bitmaps:

Glide.with(yourFragment)

.load(yourUrl)

.asBitmap()

.transform(new MyTransformation(context))

.into(yourView);

// For Gifs:

Glide.with(yourFragment)

.load(yourUrl)

.asGif()

.transform(new MyTransformation(context))

.into(yourView);

3.自定义转换器的尺寸

在上面使用过程当中没有设置尺寸值,那么转换器转换的图片尺寸怎么确定呢,

Glide实际上已经足够智能根据view的尺寸来确定转换图片的尺寸了

如果需要自定义尺寸,而不是用view和target当中的尺寸,那么可以使用override(int,int)设置相关的宽和高

4. Bitmap 再利用

为了减少垃圾收集,可以通过BitmapPool接口去释放不需要的Bitmaps,当然也可以对里面的bitmap进行再利用。

例如在一次转换中,

从pool当中得到一个bitmap

将Bitmap回设给Canvas

使用Matrix、Paint在Canvas上面绘制原始的Bitmap,或者通过一个Shader来转换一个image

4.1 不要手动地去释放一个转换的bitmap资源,也不要将transform()之后的Bitmap重新放置到BitmapPool当中去

protected Bitmap transform(BitmapPool bitmapPool, Bitmap original, int width, int height) {

Bitmap result = bitmapPool.get(width, height, Bitmap.Config.ARGB_8888);

// If no matching Bitmap is in the pool, get will return null, so we should //allocate.

if (result == null) {

// Use ARGB_8888 since we're going to add alpha to the image.

result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

}

// Create a Canvas backed by the result Bitmap.

Canvas canvas = new Canvas(result);

Paint paint = new Paint();

paint.setAlpha(128);

// Draw the original Bitmap onto the result Bitmap with a transformation.

canvas.drawBitmap(original, 0, 0, paint);

// Since we've replaced our original Bitmap, we return our new Bitmap here. Glide will

// will take care of returning our original Bitmap to the BitmapPool for us.

return result;

}

常见问题

为什么有的图片第一次加载的时候只显示占位图,第二次才显示正常的图片呢?

为什么我总会得到类似You cannot start a load for a destroyed activity这样的异常呢?

为什么我不能给加载的图片setTag()呢?

为什么?为什么?这么NB的库竟然会有这么多的问题。没错,这就是我今天要讲的重点。怎么避免上面的问题发生。

一些解决方案

1.如果你刚好使用了这个圆形Imageview库或者其他的一些自定义的圆形Imageview,而你又刚好设置了占位的话,那么,你就会遇到第一个问题。如何解决呢?

方案一:不设置占位;

方案二:使用Glide的Transformation API自定义圆形Bitmap的转换。这里是一个已有的例子;

方案三:使用下面的代码加载图片:

Glide.with(mContext)

.load(url)

.placeholder(R.drawable.loading_spinner)

.into(new SimpleTarget(width, height) {

@Override

public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {

// setImageBitmap(bitmap) on CircleImageView

}

};

2.至于第二个问题,请记住一句话:不要再非主线程里面使用Glide加载图片,如果真的使用了,请把context参数换成getApplicationContext。更多的细节请参考这个issue(https://github.com/bumptech/glide/issues/138)。

The key isViewTarget.setTagId; setting it will free up the defaultsetTagon theImageViewso you can use it as root in the item layout. It was introduced in Glide 3.6.0 inissue #370.

In your manifest add this:

android:name=".App"

Then create an application context class:

public class App extends Application {

@Override public void onCreate() {

super.onCreate();

ViewTarget.setTagId(R.id.glide_tag);

}

}

Add the following as contents forsrc/main/values/ids.xml:

(or just add the aboveinto anyresourcesxml invalues)

3.为什么不能设置Tag,是因为你使用的姿势不对哦。如何为ImageView设置Tag呢?且听我细细道来。

方案一:使用setTag(int,object)方法设置tag,具体用法如下:

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(imageViewHolder.image);

imageViewHolder.image.setTag(R.id.image_tag, i);

imageViewHolder.image.setOnClickListener(new View.OnClickListener() {

@Override

int position = (int) v.getTag(R.id.image_tag);

Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();

}

});

同时在values文件夹下新建ids.xml,添加

大功告成!

方案二:从Glide的3.6.0之后,新添加了全局设置的方法。具体方法如下:

先实现GlideMoudle接口,全局设置ViewTaget的tagId:

public class MyGlideMoudle implements GlideModule{

@Override

public void applyOptions(Context context, GlideBuilder builder) {

ViewTarget.setTagId(R.id.glide_tag_id);

}

@Override

public void registerComponents(Context context, Glide glide) {

}

}

同样,也需要在ids.xml下添加id

最后在AndroidManifest.xml文件里面添加

android:name="com.yourpackagename.MyGlideMoudle"

android:value="GlideModule" />

又可以愉快的玩耍了,嘻嘻`(∩_∩)′。

方案三:写一个继承自ImageViewTaget的类,复写它的get/setRequest方法。

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(new ImageViewTarget(imageViewHolder.image) {

@Override

protected void setResource(GlideDrawable resource) {

imageViewHolder.image.setImageDrawable(resource);

}

@Override

public void setRequest(Request request) {

imageViewHolder.image.setTag(i);

imageViewHolder.image.setTag(R.id.glide_tag_id,request);

}

@Override

public Request getRequest() {

return (Request) imageViewHolder.image.getTag(R.id.glide_tag_id);

}

});

imageViewHolder.image.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

int position = (int) v.getTag();

Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();

}

});

补充说明:

1.placeholder() 占位图或者失败图都可以直接使用R.color.white 颜色来做;

2.如果加载不出来图片的话可以试试设置下图片的宽高;

3.图片的加载并不是全缓存,而是使用的imageview的大小来的,如果想要全缓存的就可以这样:

.diskCacheStrategy(DiskCacheStrategy.ALL)

4.图片加载背景会变成绿色,加载jpg图片会出现的BUG,github上给出解决方案

Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView); 或者

Glide.with(this).fromResource().asBitmap().encoder(newBitmapEncoder(Bitmap.CompressFormat.PNG,100)).load(R.drawable.testimg).into(imageView);

详见:http://blog.csdn.net/jdsjlzx/article/details/51785100

5.圆形图片用V4包自带的处理方式:

Glide.with(context).load(imageUrl).asBitmap().fitCenter().diskCacheStrategy(DiskCacheStrategy.SOURCE)

.placeholder(R.drawable.shape_glide_round_place).error(R.drawable.no_face_circle)

.into(newBitmapImageViewTarget(imageView) {

@Override

protected voidsetResource(Bitmap resource) {

RoundedBitmapDrawable circularBitmapDrawable =

RoundedBitmapDrawableFactory.create(context.getResources(),resource);

circularBitmapDrawable.setCircular(true);

imageView.setImageDrawable(circularBitmapDrawable);

}

});

5.默认的动画可以取消掉,加上dontAnimate()。

6.清除缓存:

Glide.get(this).clearMemory();

Glide.get(this).clearDiskCache(); 需要在子线程执行

7. 加载暂时不支持显示进度,可以用占位图来显示,把占位图替换成帧动画, 如果一定要显示数字进度,可以配合Okhttp协议栈来处理。

8.动态的GIF图片加载:

Glide.with(context).load(...).asBitmap() //显示gif静态图片

Glide.with(context).load(...).asGif() //显示gif动态图片

9.本地视频快照:

Glide现在还可以把视频解码为一张图片:

Glide.with(context).load(“视频路径“)

10.对缩略图的支持:

//加载yourView1/10尺寸的缩略图,然后加载全图

Glide.with(yourFragment).load(yourUrl).thumbnail(0.1f).into(yourView)

11.对缩略图的支持:

Glide的.toBytes()和.transcode()方法允许在后台获取、解码和转换一个图片,你可以将一张图片转换成更多有用的图片格式,比如,上传一张250*250的图片

Glide.with(context)

.load(“/user/profile/photo/path”)

.asBitmap()

.toBytes()

.centerCrop()

.into(new SimpleTarget(250, 250) {

@Override

public void onResourceReady(byte[] data, GlideAnimation anim) {

// Post your bytes to a background thread and upload them here.

}

});

一些使用技巧

1.Glide.with(context).resumeRequests()和Glide.with(context).pauseRequests()

当列表在滑动的时候,调用pauseRequests()取消请求,滑动停止时,调用resumeRequests()恢复请求。这样是不是会好些呢?

2.Glide.clear()

当你想清除掉所有的图片加载请求时,这个方法可以帮助到你。

3.ListPreloader

如果你想让列表预加载的话,不妨试一下ListPreloader这个类。

一些基于Glide的优秀库

1.glide-transformations

https://github.com/wasabeef/glide-transformations

一个基于Glide的transformation库,拥有裁剪,着色,模糊,滤镜等多种转换效果,赞的不行不行的~~

2.GlidePalette

https://github.com/florent37/GlidePalette

一个可以在Glide加载时很方便使用Palette的库。

--------------------------------------------------------------------------------------------------------

Glide也提供了类似ImageLoader的暂停恢复(加载图片)方法,如下所示:

Glide.with(MainActivity.this).pauseRequests();

Glide.with(MainActivity.this).resumeRequests();

使用如下:

mLRecyclerView.setChangeScrollStateCallback(new ChangeScrollStateCallback() {

//0 表示停止滑动的状态 SCROLL_STATE_IDLE

//1表示正在滚动,用户手指在屏幕上 SCROLL_STATE_TOUCH_SCROLL

//2表示正在滑动。用户手指已经离开屏幕 SCROLL_STATE_FLING

@Override

public void change(int c) {

switch (c) {

case 2:

Glide.with(MainActivity.this).pauseRequests();

Log.d("lzx", "暂停加载" + c);

break;

case 0:

Glide.with(MainActivity.this).resumeRequests();

Log.d("lzx","恢复加载" + c);

break;

case 1:

Glide.with(MainActivity.this).resumeRequests();

Log.d("lzx", "恢复加载" + c);

break;

}

}

});

可以结合LRecyclerView使用,里面提供了滑动监听事件,详见:http://blog.csdn.net/jdsjlzx/article/details/51794220

RecyclerView使用 及 滑动时加载图片优化方案

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容