作者簡介 原創(chuàng)微信公眾號郭霖 WeChat ID: guolin_blog
現(xiàn)在Android上的圖片加載框架非常成熟狂巢,從最早的老牌圖片加載框架UniversalImageLoader习寸,到后來Google推出的Volley浦楣,再到后來的新興軍Glide和Picasso润讥,當(dāng)然還有Facebook的Fresco呛梆。每一個(gè)都非常穩(wěn)定吟税,功能也都十分強(qiáng)大搪泳。但是它們的使用場景基本都是重合的稀轨,也就是說我們基本只需要選擇其中一個(gè)來進(jìn)行學(xué)習(xí)和使用就足夠了,每一個(gè)框架都嘗試去掌握的話則有些浪費(fèi)時(shí)間岸军。
在這幾個(gè)框架當(dāng)中奋刽,我對Volley和Glide研究得比較深入瓦侮,對UniversalImageLoader、Picasso和Fresco都只是有一些基本的了解杨名。從易用性上來講脏榆,Glide和Picasso應(yīng)該都是完勝其他框架的猖毫,這兩個(gè)框架都實(shí)在是太簡單好用了台谍,大多數(shù)情況下加載圖片都是一行代碼就能解決的,而UniversalImageLoader和Fresco則在這方面略遜一些吁断。
總之趁蕊,沒有最好的框架,只有最適合自己的框架仔役。經(jīng)過多方面對比之后掷伙,我還是決定選擇了Glide來進(jìn)行研究,并且這也是Google官方推薦的圖片加載框架又兵。
說實(shí)話任柜,關(guān)于Glide的文章我已經(jīng)籌備了好久,去年這個(gè)時(shí)候本來就打算要寫了沛厨,但是一直都沒有動(dòng)筆宙地。因?yàn)槿ツ晡业拇蟛糠謺r(shí)間都放在了寫《第二行代碼》上面,只能用碎片時(shí)間來寫寫博客逆皮,但是Glide的難度遠(yuǎn)超出了我用碎片時(shí)間所能掌握的難度宅粥。當(dāng)然,這里我說的是對它的源碼進(jìn)行解析的難度电谣,不是使用上的難度秽梅,Glide的用法是很簡單的。所以剿牺,我覺得去年我寫不好Glide這個(gè)題材的文章企垦,也就一直拖到了今年。
而現(xiàn)在晒来,我花費(fèi)了大量的精力去研究Glide的源碼和各種用法钞诡,相信現(xiàn)在已經(jīng)可以將它非常好地掌握了,因此我準(zhǔn)備將我掌握的這些知識整理成一個(gè)新的系列潜索,幫忙大家更好地學(xué)習(xí)Glide臭增。這個(gè)Glide系列大概會(huì)有8篇左右文章,預(yù)計(jì)花半年時(shí)間寫完竹习,將會(huì)包括Glide的基本用法誊抛、源碼解析、高級用法整陌、功能擴(kuò)展等內(nèi)容拗窃,可能會(huì)是目前互聯(lián)網(wǎng)上最詳盡的Glide教程瞎领。
那么本篇文章是這個(gè)系列的第一篇文章,我們先來了解一下Glide的基本用法吧随夸。
開始
Glide是一款由Bump Technologies開發(fā)的圖片加載框架九默,使得我們可以在Android平臺(tái)上以極度簡單的方式加載和展示圖片。
目前宾毒,Glide最新的穩(wěn)定版本是3.7.0驼修,雖然3.8.0已經(jīng)推出預(yù)覽版了,但是暫時(shí)問題還比較多诈铛。因此乙各,我們這個(gè)系列的博客都會(huì)使用Glide 3.7.0版本來進(jìn)行講解,這個(gè)版本的Glide相當(dāng)成熟和穩(wěn)定幢竹。
要想使用Glide耳峦,首先需要將這個(gè)庫引入到我們的項(xiàng)目當(dāng)中。新建一個(gè)GlideTest項(xiàng)目焕毫,然后在app/build.gradle文件當(dāng)中添加如下依賴:
dependencies { ? ?compile'com.github.bumptech.glide:glide:3.7.0'}
另外蹲坷,Glide中需要用到網(wǎng)絡(luò)功能,因此你還得在AndroidManifest.xml中聲明一下網(wǎng)絡(luò)權(quán)限才行:
android:name="android.permission.INTERNET"/>
就是這么簡單邑飒,然后我們就可以自由地使用Glide中的任意功能了循签。
加載圖片
現(xiàn)在我們就來嘗試一下如何使用Glide來加載圖片吧。比如這是必應(yīng)上一張首頁美圖的地址:
http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg
然后我們想要在程序當(dāng)中去加載這張圖片幸乒。
那么首先打開項(xiàng)目的布局文件懦底,在布局當(dāng)中加入一個(gè)Button和一個(gè)ImageView,如下所示:
為了讓用戶點(diǎn)擊Button的時(shí)候能夠?qū)偛诺膱D片顯示在ImageView上罕扎,我們需要修改MainActivity中的代碼聚唐,如下所示:
publicclassMainActivityextendsAppCompatActivity{
ImageViewimageView;
@OverrideprotectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState); ? ?setContentView(R.layout.activity_main); ? ?imageView=(ImageView) findViewById(R.id.image); ?}
publicvoidloadImage(Viewview) {
Stringurl="http://cn.bing.com/az/hprichbg/rb/Dongdaemun_ZH-CN10736487148_1920x1080.jpg";
Glide.with(this).load(url).into(imageView); ?}}
沒錯(cuò),就是這么簡單∏徽伲現(xiàn)在我們來運(yùn)行一下程序杆查,效果如下圖所示:
可以看到,一張網(wǎng)絡(luò)上的圖片已經(jīng)被成功下載臀蛛,并且展示到ImageView上了亲桦。
而我們到底做了什么?實(shí)際上核心的代碼就只有這一行而已:
Glide.with(this).load(url).into(imageView);
千萬不要小看這一行代碼浊仆,實(shí)際上僅僅就這一行代碼客峭,你已經(jīng)可以做非常非常多的事情了,包括加載網(wǎng)絡(luò)上的圖片抡柿、加載手機(jī)本地的圖片舔琅、加載應(yīng)用資源中的圖片等等。
下面我們就來詳細(xì)解析一下這行代碼洲劣。
首先备蚓,調(diào)用Glide.with()方法用于創(chuàng)建一個(gè)加載圖片的實(shí)例课蔬。with()方法可以接收Context、Activity或者Fragment類型的參數(shù)郊尝。也就是說我們選擇的范圍非常廣二跋,不管是在Activity還是Fragment中調(diào)用with()方法,都可以直接傳this流昏。那如果調(diào)用的地方既不在Activity中也不在Fragment中呢扎即?也沒關(guān)系,我們可以獲取當(dāng)前應(yīng)用程序的ApplicationContext横缔,傳入到with()方法當(dāng)中铺遂。注意with()方法中傳入的實(shí)例會(huì)決定Glide加載圖片的生命周期,如果傳入的是Activity或者Fragment的實(shí)例茎刚,那么當(dāng)這個(gè)Activity或Fragment被銷毀的時(shí)候,圖片加載也會(huì)停止撤逢。如果傳入的是ApplicationContext膛锭,那么只有當(dāng)應(yīng)用程序被殺掉的時(shí)候,圖片加載才會(huì)停止蚊荣。
接下來看一下load()方法初狰,這個(gè)方法用于指定待加載的圖片資源。Glide支持加載各種各樣的圖片資源互例,包括網(wǎng)絡(luò)圖片奢入、本地圖片、應(yīng)用資源媳叨、二進(jìn)制流腥光、Uri對象等等。因此load()方法也有很多個(gè)方法重載糊秆,除了我們剛才使用的加載一個(gè)字符串網(wǎng)址之外武福,你還可以這樣使用load()方法:
//加載本地圖片F(xiàn)ilefile=getImagePath();
Glide.with(this).load(file).into(imageView);
//加載應(yīng)用資源
intresource=R.drawable.image;
Glide.with(this).load(resource).into(imageView);
//加載二進(jìn)制流
byte[] image=getImageBytes();
Glide.with(this).load(image).into(imageView);
//加載Uri對象
UriimageUri=getImageUri();
Glide.with(this).load(imageUri).into(imageView);
最后看一下into()方法,這個(gè)方法就很簡單了痘番,我們希望讓圖片顯示在哪個(gè)ImageView上捉片,把這個(gè)ImageView的實(shí)例傳進(jìn)去就可以了。當(dāng)然汞舱,into()方法不僅僅是只能接收ImageView類型的參數(shù)伍纫,還支持很多更豐富的用法,不過那個(gè)屬于高級技巧昂芜,我們會(huì)在后面的文章當(dāng)中學(xué)習(xí)莹规。
那么回顧一下Glide最基本的使用方式,其實(shí)就是關(guān)鍵的三步走:先with()说铃,再load()访惜,最后into()嘹履。熟記這三步,你就已經(jīng)入門Glide了债热。
占位圖
現(xiàn)在我們來學(xué)一些Glide的擴(kuò)展內(nèi)容砾嫉。其實(shí)剛才所學(xué)的三步走就是Glide最核心的東西,而我們后面所要學(xué)習(xí)的所有東西都是在這個(gè)三步走的基礎(chǔ)上不斷進(jìn)行擴(kuò)展而已窒篱。
觀察剛才加載網(wǎng)絡(luò)圖片的效果焕刮,你會(huì)發(fā)現(xiàn),點(diǎn)擊了Load Image按鈕之后墙杯,要稍微等一會(huì)圖片才會(huì)顯示出來配并。這其實(shí)很容易理解,因?yàn)閺木W(wǎng)絡(luò)上下載圖片本來就是需要時(shí)間的高镐。那么我們有沒有辦法再優(yōu)化一下用戶體驗(yàn)?zāi)馗刃慨?dāng)然可以,Glide提供了各種各樣非常豐富的API支持嫉髓,其中就包括了占位圖功能观腊。
顧名思義,占位圖就是指在圖片的加載過程中算行,我們先顯示一張臨時(shí)的圖片梧油,等圖片加載出來了再替換成要加載的圖片。
下面我們就來學(xué)習(xí)一下Glide占位圖功能的使用方法州邢,首先我事先準(zhǔn)備好了一張loading.jpg圖片儡陨,用來作為占位圖顯示。然后修改Glide加載部分的代碼量淌,如下所示:
Glide.with(this) ? ? .load(url) ? ? .placeholder(R.drawable.loading) ? ? .into(imageView);
沒錯(cuò)骗村,就是這么簡單。我們只是在剛才的三步走之間插入了一個(gè)placeholder()方法类少,然后將占位圖片的資源id傳入到這個(gè)方法中即可叙身。另外,這個(gè)占位圖的用法其實(shí)也演示了Glide當(dāng)中絕大多數(shù)API的用法硫狞,其實(shí)就是在load()和into()方法之間串接任意想添加的功能就可以了信轿。
不過如果你現(xiàn)在重新運(yùn)行一下代碼并點(diǎn)擊Load Image,很可能是根本看不到占位圖效果的残吩。因?yàn)镚lide有非常強(qiáng)大的緩存機(jī)制财忽,我們剛才加載那張必應(yīng)美圖的時(shí)候Glide自動(dòng)就已經(jīng)將它緩存下來了,下次加載的時(shí)候?qū)?huì)直接從緩存中讀取泣侮,不會(huì)再去網(wǎng)絡(luò)下載了即彪,因而加載的速度非常快,所以占位圖可能根本來不及顯示隶校。
因此這里我們還需要稍微做一點(diǎn)修改漏益,來讓占位圖能有機(jī)會(huì)顯示出來,修改代碼如下所示:
Glide.with(this) ? ? .load(url) ? ? .placeholder(R.drawable.loading) ? ? .diskCacheStrategy(DiskCacheStrategy.NONE) ? ? .into(imageView);
可以看到深胳,這里串接了一個(gè)diskCacheStrategy()方法绰疤,并傳入DiskCacheStrategy.NONE參數(shù),這樣就可以禁用掉Glide的緩存功能舞终。
關(guān)于Glide緩存方面的內(nèi)容我們將會(huì)在后面的文章進(jìn)行詳細(xì)的講解轻庆,這里只是為了測試占位圖功能而加的一個(gè)額外配置,暫時(shí)你只需要知道禁用緩存必須這么寫就可以了敛劝。
現(xiàn)在重新運(yùn)行一下代碼余爆,效果如下圖所示:
可以看到,當(dāng)點(diǎn)擊Load Image按鈕之后會(huì)立即顯示一張占位圖夸盟,然后等真正的圖片加載完成之后會(huì)將占位圖替換掉蛾方。
當(dāng)然,這只是占位圖的一種满俗,除了這種加載占位圖之外转捕,還有一種異常占位圖。異常占位圖就是指唆垃,如果因?yàn)槟承┊惓G闆r導(dǎo)致圖片加載失敗,比如說手機(jī)網(wǎng)絡(luò)信號不好痘儡,這個(gè)時(shí)候就顯示這張異常占位圖辕万。
異常占位圖的用法相信你已經(jīng)可以猜到了,首先準(zhǔn)備一張error.jpg圖片沉删,然后修改Glide加載部分的代碼渐尿,如下所示:
Glide.with(this) ? ? .load(url) ? ? .placeholder(R.drawable.loading) ? ? .error(R.drawable.error) ? ? .diskCacheStrategy(DiskCacheStrategy.NONE) ? ? .into(imageView);
很簡單,這里又串接了一個(gè)error()方法就可以指定異常占位圖了矾瑰。
現(xiàn)在你可以將圖片的url地址修改成一個(gè)不存在的圖片地址,或者干脆直接將手機(jī)的網(wǎng)絡(luò)給關(guān)了殴穴,然后重新運(yùn)行程序凉夯,效果如下圖所示:
這樣我們就把Glide提供的占位圖功能都掌握了。
指定圖片格式
我們還需要再了解一下Glide另外一個(gè)強(qiáng)大的功能采幌,那就是Glide是支持加載GIF圖片的劲够。這一點(diǎn)確實(shí)非常牛逼,因?yàn)橄啾戎翵ake Warton曾經(jīng)明確表示過休傍,Picasso是不會(huì)支持加載GIF圖片的征绎。
而使用Glide加載GIF圖并不需要編寫什么額外的代碼,Glide內(nèi)部會(huì)自動(dòng)判斷圖片格式磨取。比如這是一張GIF圖片的URL地址:
http://p1.pstatp.com/large/166200019850062839d3
我們只需要將剛才那段加載圖片代碼中的URL地址替換成上面的地址就可以了人柿,現(xiàn)在重新運(yùn)行一下代碼柴墩,效果如下圖所示:
也就是說,不管我們傳入的是一張普通圖片凫岖,還是一張GIF圖片江咳,Glide都會(huì)自動(dòng)進(jìn)行判斷,并且可以正確地把它解析并展示出來隘截。
但是如果我想指定圖片的格式該怎么辦呢扎阶?就比如說,我希望加載的這張圖必須是一張靜態(tài)圖片婶芭,我不需要Glide自動(dòng)幫我判斷它到底是靜圖還是GIF圖东臀。
想實(shí)現(xiàn)這個(gè)功能仍然非常簡單,我們只需要再串接一個(gè)新的方法就可以了犀农,如下所示:
Glide.with(this) ? ? .load(url) ? ? .asBitmap() ? ? .placeholder(R.drawable.loading) ? ? .error(R.drawable.error) ? ? .diskCacheStrategy(DiskCacheStrategy.NONE) ? ? .into(imageView);
可以看到惰赋,這里在load()方法的后面加入了一個(gè)asBitmap()方法,這個(gè)方法的意思就是說這里只允許加載靜態(tài)圖片呵哨,不需要Glide去幫我們自動(dòng)進(jìn)行圖片格式的判斷了赁濒。
現(xiàn)在重新運(yùn)行一下程序,效果如下圖所示:
由于調(diào)用了asBitmap()方法孟害,現(xiàn)在GIF圖就無法正常播放了拒炎,而是會(huì)在界面上顯示第一幀的圖片。
那么類似地挨务,既然我們能強(qiáng)制指定加載靜態(tài)圖片击你,就也能強(qiáng)制指定加載動(dòng)態(tài)圖片。比如說我們想要實(shí)現(xiàn)必須加載動(dòng)態(tài)圖片的功能谎柄,就可以這樣寫:
Glide.with(this) ? ? .load(url) ? ? .asGif() ? ? .placeholder(R.drawable.loading) ? ? .error(R.drawable.error) ? ? .diskCacheStrategy(DiskCacheStrategy.NONE) ? ? .into(imageView);
這里調(diào)用了asGif()方法替代了asBitmap()方法丁侄,很好理解,相信不用我多做什么解釋了朝巫。
那么既然指定了只允許加載動(dòng)態(tài)圖片鸿摇,如果我們傳入了一張靜態(tài)圖片的URL地址又會(huì)怎么樣呢?試一下就知道了劈猿,將圖片的URL地址改成剛才的必應(yīng)美圖拙吉,然后重新運(yùn)行代碼,效果如下圖所示糙臼。
動(dòng)圖
沒錯(cuò)庐镐,如果指定了只能加載動(dòng)態(tài)圖片,而傳入的圖片卻是一張靜圖的話变逃,那么結(jié)果自然就只有加載失敗嘍必逆。
指定圖片大小
實(shí)際上,使用Glide在絕大多數(shù)情況下我們都是不需要指定圖片大小的。
在學(xué)習(xí)本節(jié)內(nèi)容之前名眉,你可能還需要先了解一個(gè)概念粟矿,就是我們平時(shí)在加載圖片的時(shí)候很容易會(huì)造成內(nèi)存浪費(fèi)。什么叫內(nèi)存浪費(fèi)呢损拢?比如說一張圖片的尺寸是1000*1000像素陌粹,但是我們界面上的ImageView可能只有200*200像素,這個(gè)時(shí)候如果你不對圖片進(jìn)行任何壓縮就直接讀取到內(nèi)存中福压,這就屬于內(nèi)存浪費(fèi)了掏秩,因?yàn)槌绦蛑懈揪陀貌坏竭@么高像素的圖片。
而使用Glide荆姆,我們就完全不用擔(dān)心圖片內(nèi)存浪費(fèi)蒙幻,甚至是內(nèi)存溢出的問題。因?yàn)镚lide從來都不會(huì)直接將圖片的完整尺寸全部加載到內(nèi)存中胆筒,而是用多少加載多少邮破。Glide會(huì)自動(dòng)判斷ImageView的大小,然后只將這么大的圖片像素加載到內(nèi)存當(dāng)中仆救,幫助我們節(jié)省內(nèi)存開支抒和。
也正是因?yàn)镚lide是如此的智能,所以剛才在開始的時(shí)候我就說了彤蔽,在絕大多數(shù)情況下我們都是不需要指定圖片大小的摧莽,因?yàn)镚lide會(huì)自動(dòng)根據(jù)ImageView的大小來決定圖片的大小。
不過顿痪,如果你真的有這樣的需求范嘱,必須給圖片指定一個(gè)固定的大小,Glide仍然是支持這個(gè)功能的员魏。修改Glide加載部分的代碼,如下所示:
Glide.with(this) ? ? .load(url) ? ? .placeholder(R.drawable.loading) ? ? .error(R.drawable.error) ? ? .diskCacheStrategy(DiskCacheStrategy.NONE) ? ? .override(100,100) ? ? .into(imageView);
仍然非常簡單叠聋,這里使用override()方法指定了一個(gè)圖片的尺寸撕阎,也就是說,Glide現(xiàn)在只會(huì)將圖片加載成100*100像素的尺寸碌补,而不會(huì)管你的ImageView的大小是多少了虏束。
好了,今天是我們這個(gè)Glide系列的第一篇文章厦章,寫了這么多內(nèi)容已經(jīng)算是挺不錯(cuò)的了≌蛟龋現(xiàn)在你已經(jīng)了解了Glide的基本用法,當(dāng)然也是一些最常用的用法袜啃。在本系列的第二篇文章中汗侵,我們會(huì)嘗試去分析Glide的源碼,研究一下在這些基本用法的背后,Glide到底執(zhí)行了什么神奇的操作晰韵,能夠使得我們加載圖片變得這么簡單发乔?敬請期待。
完雪猪。栏尚。。只恨。译仗。。官觅。纵菌。。缰猴。产艾。。滑绒。闷堡。。疑故。杠览。。纵势。踱阿。。
文章原創(chuàng)作者GuoLin 書籍推薦
郭林大神原創(chuàng)android 書籍:《第一行代碼 android》