Glide是Google官方推薦使用的圖片加載庫子漩。大哥已經(jīng)表揚(yáng)人家了铃岔,小弟們能不學(xué)習(xí)學(xué)習(xí)嗎茫孔?如果你現(xiàn)在還在使用Picasso隘蝎,那么先參考這篇文章對(duì)比一下Picasso和Glide晒哄。
添加依賴:
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0' # 添加Glide依賴
compile 'jp.wasabeef:glide-transformations:2.0.0' # 可選睁宰,添加Glide變換第三方實(shí)現(xiàn)依賴肪获。如果你對(duì)顯示的圖片有什么模糊啊,圓角什么的變換柒傻,那就添加了吧孝赫,省很多事。
compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0' # 可選红符,沒用過青柄,感興趣GitHub上看看吧.
}
應(yīng)付95%使用場(chǎng)合:
不說廢話,直接上代碼:
Glide
.with(context)
.load(imageUrl) # 也可以是來自resouceId预侯、File致开、Uri代表的圖片資源。
.placeHolder(resourceId) # 占位符萎馅,就是圖片從開始請(qǐng)求到最后完全加載双戳,這段時(shí)間顯示的默認(rèn)圖片。
.error(erroResouceId) #請(qǐng)求圖片發(fā)生異常的錯(cuò)誤圖片糜芳。
.into(targetImageView)
That's it飒货!如果我沒有估計(jì)錯(cuò),這幾行代碼足夠?qū)Ω俄?xiàng)目中大多數(shù)的圖片加載需求了耍目。對(duì)于ListView膏斤、GridView、RecyclerView等也不用擔(dān)心邪驮,加載圖片也就是這些代碼莫辨,其它的什么條目不可見的時(shí)候自動(dòng)取消加載圖片的請(qǐng)求等等Glide都已經(jīng)替我們解決好了。
還用一點(diǎn)我想大聲對(duì)你說的就是Glide還可以加載gif毅访。什么都不用改沮榜,只要你給的圖片資源是gif人家就能放。有時(shí)候服務(wù)器那邊腦子進(jìn)水給你一個(gè)gif圖片的url喻粹,但實(shí)際上是張靜態(tài)圖片蟆融,邏輯上這個(gè)應(yīng)該算是加載錯(cuò)誤圖片資源,應(yīng)該讓error()顯神威守呜,不過Glide無法自動(dòng)探知你就是想加載gif而其它類型都是錯(cuò)誤型酥,所以你得用asGif()明確告訴Glide:這里我就是要gif,其它類型的圖片都是錯(cuò)誤:
Glide
.with( context )
.load( gifUrl )
.asGif()
.error( R.drawable.full_cake )
.into( imageViewGif );
還有時(shí)候你只想顯示靜態(tài)圖片查乒,但你是在怕了服務(wù)器那邊弥喉,搞不好又進(jìn)水在需要靜態(tài)圖片的地方給了gif,沒關(guān)系玛迄,使用asBitmap()如果來的是一張gif只會(huì)顯示它的第一幀:
Glide
.with( context )
.load( gifUrl )
.asBitmap()
.into( imageViewGifAsBitmap );
注意一點(diǎn)的就是你給with()方法的context實(shí)例由境,Glide的生命周期會(huì)綁定到這個(gè)context的實(shí)例上,Glide在context不同生命周期回調(diào)中有不同的處理,例如圖片請(qǐng)求在onStop()中會(huì)暫停虏杰,在onStart()中又會(huì)重新請(qǐng)求讥蟆;gif在onStop()會(huì)暫停播放等等。所以Application纺阔、Activity瘸彤、Fragment、Service等都可以提供context笛钝,你就要根據(jù)自己的情況慎重選擇適合的context實(shí)例了钧栖。
Glide還可以播放本地視頻,只是本地Android可以解碼的視頻類型哦婆翔!
String filePath = "/storage/emulated/0/Pictures/example_video.mp4";
Glide
.with( context )
.load( Uri.fromFile( new File( filePath ) ) )
.into( imageViewGifAsBitmap );
有時(shí)候一個(gè)頁面有很多圖片,但有主次之分掏婶。你希望主要的圖片先被加載出來啃奴,之后在加載次要的圖片。你可以通過指定請(qǐng)求優(yōu)先級(jí)來實(shí)現(xiàn)這樣的需求雄妥,有一點(diǎn)需要銘記在心的是:指定優(yōu)先級(jí)只不過讓優(yōu)先級(jí)高的先請(qǐng)求最蕾,但由于圖片大小,圖片處理時(shí)間等等也需要時(shí)間老厌,所以不能保證最后請(qǐng)求優(yōu)先級(jí)高的一定最先顯示瘟则。
??先來看一下,Glide都有哪些請(qǐng)求優(yōu)先級(jí)別:
Priority.LOW
Priority.NORMAL
Priority.HIGH
Priority.IMMEDIATE
見名知義枝秤,不解釋了醋拧。最后用priority()指定請(qǐng)求優(yōu)先級(jí):
Glide
.with( context )
.load( imageUrl )
.priority( Priority.HIGH )
.into( imageViewHero );
默認(rèn)情況下根據(jù)給定的ImageView大小來決定要加載的圖片大小。當(dāng)然淀弹,你也可以使用override(horizontalSize, verticalSize)指定要加載的圖片大械ず尽:
Glide
.with(context)
.load(imageUrl)
.override(600, 200)
.into(imageViewResize);
不過這樣做可能會(huì)破壞原圖片的比例。所以你指定大小的時(shí)候薇溃,所以你還可以用fitCenter()或centerCrop()來縮放圖片菌赖,用以達(dá)到比較好的視覺效果。
Glide
.with(context)
.load(imageUrl)
.override(600, 200)
.centerCrop() #縮放圖片以滿足ImageView的尺寸沐序,超過ImageView的部分將會(huì)被裁剪掉琉用,因此最終圖片可能不完全顯示。
.into(imageViewResizeCenterCrop);
Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200)
.fitCenter() #保持原圖片的比例進(jìn)行縮放策幼,直到可以在ImageView中尺寸區(qū)域內(nèi)完全顯示圖片邑时。圖片能夠完全顯示,比例保持不變垄惧,但是可能圖片無法完全覆蓋ImageView的區(qū)域刁愿。
.into(imageViewResizeFitCenter);
圖片從無到完全顯示,Glide默認(rèn)是有過渡動(dòng)畫的到逊,還可用crossFade(int duration)來自定義過渡動(dòng)畫的毫秒數(shù)铣口。當(dāng)然如果你就是喜歡嚇用戶滤钱,想要突然間把圖片加載出來了,可以用dontAnimate()取消過渡動(dòng)畫:
Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.future_studio_launcher)
.dontAnimate()
.into(imageViewFade);
Glide緩存使用
不用說脑题,Glide當(dāng)然會(huì)在內(nèi)存和本地緩存已經(jīng)請(qǐng)求過的圖片件缸。但有時(shí)一個(gè)url對(duì)應(yīng)的圖片內(nèi)容變動(dòng)比較大,不需要把它緩存在內(nèi)存或者本地叔遂。那么可以使用skipMemoryCache(true):
Glide
.with( context )
.load( imageUrl)
.skipMemoryCache( true )
.into( imageViewInternet );
當(dāng)然這只是告訴Glide圖片不要在內(nèi)存中緩存他炊,Glide在本地還會(huì)照樣緩存請(qǐng)求過的圖片的∫鸭瑁可以用diskCacheStrategy()取消本地緩存痊末。要明白一點(diǎn)的是Glide對(duì)于一張從網(wǎng)絡(luò)請(qǐng)求來的圖片,Glide除了會(huì)在本地存儲(chǔ)原始尺寸的圖片哩掺,還會(huì)緩存加載到ImageView的較小尺寸的圖片凿叠。因此diskCacheStrategy()可以接受:
- DiskCacheStrategy.NONE 本地什么都不緩存
- DiskCacheStrategy.SOURCE 本地緩存原始的全尺寸圖片
- DiskCacheStrategy.RESULT 本地緩存加載到ImageView上的較小尺寸的圖片
- DiskCacheStrategy.ALL 默認(rèn)緩存全部尺寸的圖片
Glide
.with( context )
.load( imageUrl)
.diskCacheStrategy( DiskCacheStrategy.SOURCE )
.into( imageViewFile );
自定義變換:
有時(shí)候在圖片顯示之前,會(huì)需要對(duì)圖片進(jìn)行一些變換嚼吞,最常出現(xiàn)的需求就是將圖片模糊盒件。自定義變換主要就是實(shí)現(xiàn)Transformation接口,要知道我們的Glide還可以比方gif和視頻舱禽,因此實(shí)現(xiàn)這個(gè)接口難度會(huì)大很多炒刁。當(dāng)然如果只是針對(duì)靜態(tài)圖片,實(shí)現(xiàn)BitmapTransformation會(huì)容易很多:
public class BlurTransformation extends BitmapTransformation {
private RenderScript rs;
public BlurTransformation(Context context) {
super( context );
rs = RenderScript.create( context );
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
Bitmap blurredBitmap = toTransform.copy( Bitmap.Config.ARGB_8888, true );
Allocation input = Allocation.createFromBitmap(
rs,
blurredBitmap,
Allocation.MipmapControl.MIPMAP_FULL,
Allocation.USAGE_SHARED
);
Allocation output = Allocation.createTyped(rs, input.getType());
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setInput(input);
script.setRadius(10);
script.forEach(output);
output.copyTo(blurredBitmap);
toTransform.recycle();
return blurredBitmap;
}
@Override
public String getId() {
return "blur";
}
}
我們?cè)趖ransform方法里可以拿到要顯示的圖片toTransform以及ta的尺寸(outWidth, outHeight)誊稚,接下來就可以發(fā)揮想象力處理圖片然后把處理后的圖片返回就像翔始。關(guān)于上面圖片模糊具體可以看這篇文章。然后就可以用transform()片吊、或者bitmapTransform()方法來使用我們自定義的變換了:
Glide
.with( context )
.load( imageUrl)
.transform( new BlurTransformation( context ) )
//.bitmapTransform( new BlurTransformation( context ) ) #這個(gè)方法接受的變換只針對(duì)bitmap绽昏。
.into( imageView1 );
如果你要進(jìn)行多種變換,transform()和bitmapTransform()可接受多個(gè)參數(shù):
Glide
.with( context )
.load( imageUrl )
.transform( new GreyscaleTransformation( context ), new BlurTransformation( context ) )
.into( imageView2 );
在最開始添加依賴時(shí)說過已經(jīng)有好人實(shí)現(xiàn)了許多圖片變換俏脊,看看這個(gè)glide-transformations項(xiàng)目全谤。是不是感覺瞬間輕松許多。
自定義動(dòng)畫:
之前已經(jīng)提到的crossFade()是個(gè)漸變動(dòng)畫爷贫,現(xiàn)在我們還想要自己定義的更帥的動(dòng)畫认然。可以使用animate()方法漫萄,ta可以接受一個(gè)動(dòng)畫的資源文件:
#R.anim.scale
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="@android:integer/config_longAnimTime"
android:fromXScale="0.1"
android:fromYScale="0.1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1"/>
</set>
Glide
.with( context )
.load( eatFoodyImages[0] )
.animate( android.R.anim.slide_in_left )
.into( imageView1 );ava
另外animate()還接受一個(gè)ViewPropertyAnimation.Animator實(shí)現(xiàn):
ViewPropertyAnimation.Animator animationObject = new ViewPropertyAnimation.Animator() {
@Override
public void animate(View view) {
view.setAlpha( 0f );
ObjectAnimator fadeAnim = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f );
fadeAnim.setDuration( 2500 );
fadeAnim.start();
}
};
只要實(shí)現(xiàn)一個(gè)animate方法就可以啦卷员。animate方法中的view就是傳入into方法里的ImageView對(duì)象:
Glide
.with( context )
.load( imageUrl )
.animate( animationObject )
.into( imageView2 );
我這里好像發(fā)現(xiàn)當(dāng)調(diào)用了crossFade()方法,animate()方法定義的動(dòng)畫好像不會(huì)被調(diào)用腾务,也就是沒有效果毕骡。另外好像過渡動(dòng)畫只對(duì)ImageView對(duì)象有效果,當(dāng)傳入into()方法的ViewTarget類型對(duì)象,crossFade()和animate()好像都不會(huì)調(diào)用未巫。這個(gè)還有待繼續(xù)驗(yàn)證窿撬。
回調(diào):SimpleTarget和ViewTarget:
Target接口代表Glide中資源最終被加載到的地方并且可以毀掉Glide中的生命周期方法。Target可以回調(diào)的生命周期方法有:
- onLoadStarted
- onResourceReady
- onLoadCleared
- onLoadFailed
??典型的生命周期是:onLoadStarted -> onResourceReady 或者 onLoadFailed -> onLoadCleared叙凡。實(shí)際可能某些方法不會(huì)調(diào)用劈伴,例如加入直接從內(nèi)存緩存中加載資源,onLoadStarted方法便不會(huì)被調(diào)用了握爷。
??這里介紹兩個(gè)Target實(shí)現(xiàn)類跛璧,同時(shí)也代表著兩個(gè)常見的需求。
??有時(shí)候我們只是希望獲得一張圖片新啼,而不想把它加載到什么地方上顯示追城,對(duì)于這個(gè)需求使用SimpleTarget最合適不過了。
private SimpleTarget target = new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
//在這里我們就可以獲得加載的資源了燥撞,當(dāng)然這里是一個(gè)bitmap漓柑。
}
};
private void loadImageSimpleTarget() {
Glide
.with( context )
.load( imageUrl)
.asBitmap()
.into( target ); //使用Target。
}
這里有點(diǎn)要在強(qiáng)調(diào)一下叨吮,就是關(guān)于傳給with()方法的context實(shí)例。要記得Glide的生命周期會(huì)和這個(gè)context實(shí)例的生命周期相綁定瞬矩。所以可能會(huì)有這樣的場(chǎng)景:在activityA中獲得圖片茶鉴,你把這個(gè)activityA傳給with()方法,然后還沒獲得圖片景用,用戶跳轉(zhuǎn)到activityB涵叮,在activityB要顯示這個(gè)圖片。很顯然activityA在回調(diào)onStop()的時(shí)候Glide也停止請(qǐng)求那張圖片伞插,所以在activityB中也就沒有圖片可顯示啦割粮。
還可以指定SimpleTarget獲得的資源的尺寸:
private SimpleTarget target2 = new SimpleTarget<Bitmap>( 250, 250 ) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
imageView2.setImageBitmap( bitmap );
}
};
另一個(gè)需求那就更常見了,而且也更迫切媚污。當(dāng)你自定義一個(gè)view的時(shí)候舀瓢,同時(shí)這個(gè)view還不是繼承ImageView但是ta也有顯示圖片的需要,怎么辦耗美?用ViewTarget來辦:
//自定義的一個(gè)ViewGroup
public class CustomView extends FrameLayout {
ImageView iv;
TextView tv;
public void initialize(Context context) {
inflate( context, R.layout.custom_view_, this );
iv = (ImageView) findViewById( R.id.custom_view_image );
tv = (TextView) findViewById( R.id.custom_view_text );
}
public CustomView(Context context, AttributeSet attrs) {
super( context, attrs );
initialize( context );
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super( context, attrs, defStyleAttr );
initialize( context );
}
public void setImage(Drawable drawable) {
iv = (ImageView) findViewById( R.id.custom_view_image );
iv.setImageDrawable( drawable );
}
}
CustomView customView = (CustomView) findViewById( R.id.custom_view );
//定義一個(gè)ViewTarget京髓,注意傳入自定義View對(duì)象,還有ViewTarget的泛型商架。
viewTarget = new ViewTarget<CustomView, GlideDrawable>( customView ) {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
//這里我們獲得傳入的自定義View堰怨,在這個(gè)自定義View中我們寫了該View設(shè)置圖片的方法。
this.view.setImage( resource.getCurrent() );
}
};
Glide
.with( context.getApplicationContext() )
.load( eatFoodyImages[2] )
.into( viewTarget ); //使用Target蛇摸。
調(diào)試和錯(cuò)誤處理:
可以使用ADB命令來查看資源請(qǐng)求時(shí)的日志:
adb shell setprop log.tag.GenericRequest DEBUG [還可選VERBOSE备图,INFO,WARN,ERROR日志級(jí)別]
雖然有error()幫我們處理請(qǐng)求圖片出錯(cuò)時(shí)的情況揽涮,但有時(shí)我們也想自己作一些處理抠藕,最起碼得看看發(fā)生了什么是吧。用listener()注冊(cè)一個(gè)RequestListener便可以得到請(qǐng)求資源時(shí)的一些關(guān)鍵回調(diào)方法:
private RequestListener<String, GlideDrawable> requestListener = new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
//獲得異常你想怎么處理隨你的便利绞吁。
return false; //如果返回true幢痘,Glide認(rèn)為你已經(jīng)處理好了這個(gè)異常,那么error()方法也就不會(huì)調(diào)用了家破。
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
};
Glide
.with( context )
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.listener( requestListener ) //注冊(cè)監(jiān)聽器颜说。
.error( R.drawable.cupcake )
.into( imageViewPlaceholder );
當(dāng)然Glide還有其它的監(jiān)聽器也很有用,例如LifecycleListener就可以在Glide里監(jiān)聽Fragment和Activity的onStart()汰聋、onStop()和onDestroy()回調(diào)门粪。
集成自己的網(wǎng)絡(luò)庫:
你可以使用自己的網(wǎng)絡(luò)庫,但這個(gè)細(xì)講起來還比較復(fù)雜烹困。這里介紹當(dāng)你的工程使用的是OkHttp或者Volley的情況玄妈,稍微配置一下build.gradle,其它什么都不用做了:
dependencies {
// 其它配置
compile 'com.github.bumptech.glide:glide:3.7.0'
// 特別注意這個(gè)髓梅,就是ta讓你可以使用OkHttp作為自己的網(wǎng)絡(luò)請(qǐng)求庫拟蜻。
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'
compile 'com.squareup.okhttp:okhttp:2.7.5'
// 針對(duì)Volley的配置 。
//compile 'com.github.bumptech.glide:volley-integration:1.4.0@aar'
//compile 'com.mcxiaoke.volley:library:1.0.8'
//針對(duì)OkHttp3的配置枯饿。
//compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
//compile 'com.squareup.okhttp3:okhttp:3.2.0'
}