源碼位置:http://square.github.io/picasso/
源碼解析網站: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0731/1639.html
以下轉自: http://www.bkjia.com/Androidjc/1004886.html
在Android開發中,常需要從遠程獲取圖片並顯示在客戶端,當然我們可以使用原生HttpUrlConnection和AsyncTask等操作來完成,但並不推薦,因爲這樣不僅需要我們編寫大量的代碼,還需要處理緩存和下載管理等,最好自己封裝成庫或者採用第三方庫;
Picasso:A Powerful Image Downloading and Caching Library for Android
根據名字就知道它是跟什麼相關了(Picasso:畢加索)它的基本操作非常簡單喔
文章主要介紹如何通過基本的HttpUrlConnection和AcyncTask實現獲取遠程圖片;以及如何使用Picasso框架來實現,最後作爲練習實現一個小Demo,希望對正在學習Android網絡操作的同學有所幫助;
1,使用HttpUrlConnection和AsyncTask實現遠程圖片下載
使用HttpUrlConnection和AsyncTask獲取遠程圖片,需要以下幾步:
HttpUrlConnection connection=url.openConnection();//url代表圖片地址
InputStream in=connection.getInputStream();
Bitmap bitmap=BitmapFactory.decodeStream(in);
imageView.setBitmap(bitmap)
我們知道在主線程中是無法執行以上網絡操作的,所以需要AsyncTask,將耗時操作運行在後臺線程中,對於HttpUrlConnection和AsyncTask具體操作不熟悉的可以看看我寫的上一篇文章:Android網絡編程 HttpUrlConnection HttpClient AsyncTask;
以下是使用上述代碼完成圖片下載的示例:
public class MainActivity extends Activity {
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image);
String url = "http://www.jycoder.com/json/Image/1.jpg";
// 執行Task
new ImageDownloadTask(ivBasicImage).execute(url);
}
//自定義獲取圖片Task
private class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {
ImageView imageView;
public ImageDownloadTask(ImageView imageView) {
this.imageView = imageView;
}
protected Bitmap doInBackground(String... addresses) {
Bitmap bitmap = null;
InputStream in;
try {
// 建立URL連接
URL url = new URL(addresses[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 打開輸入流
conn.connect();
in = conn.getInputStream();
// 編碼輸入流
bitmap = BitmapFactory.decodeStream(in);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(in != null)
in.close();
}
return bitmap;
}
// Task執行完畢,返回bitmap
@Override
protected void onPostExecute(Bitmap result) {
// Set bitmap image for the result
imageView.setImageBitmap(result);
}
}
}
2,Picasso框架
什麼是Picasso:
強大的圖片下載和緩存的第三方庫;我覺得這就是對它最準確的描述了,至於其他特性,可以參見官網介紹:Picasso
如何使用Picasso
android update project -p
ant jar
2.Picasso的基本用法:
將Picasso添加進項目後,要使用它非常簡單,只需要一行代碼就能搞定:
Picasso.with(context).load(imageUrl).into(imageView);
短短的一行代碼爲我們解決了很多問題:
- 自動將圖像緩存在本地
- 通過圖片壓縮轉換以減少內存消耗
- 自動處理了ImageView的回收,即自動取消不在視野範圍內的ImageView視圖資源的加載;
3.適配器:適配器自動發現和重用以前取消的下載:
@Override
public void getView(int position, View convertView, ViewGroup parent) {
SquaredImageView view = (SquaredImageView) convertView;
if (view == null) {
view = new SquaredImageView(context);
}
String url = getItem(position);
Picasso.with(context).load(url).into(view);
}
4.圖像格式轉換:很多時候需要將圖片進行格式轉換或者剪裁以節省內存或者達到我們的佈局效果:
剪裁大小:
Picasso.with(context).load(imageUrl).resize(50,50).centerCrop().into(imageView);
自定義格式轉換:爲了實現更多你想要圖片轉換的效果,你可以自己實現一個實現了Transformation接口的類,然後將其對象傳遞給transform()方法:
public calss myTransformation implements Transformation{
@Overrride
public Bitmap transform(Bitmap source){
//對source實現自定義裁剪
}
@Override
public String key(){
return "square()";
}
}
5.佔位符圖片:所謂的佔位符圖像即當圖片未正常顯示時默認的圖片,通過placeholder()設置,Picasso也支持設置圖片顯示錯誤時顯示的默認圖片,通過error()設置:
Picasso.wint(context).load(imageUrl).placeholder(R.drawable.image_placeholder).error(R.drawable.image_error_placeholder).into(imageView);
6.載入本地資源:除了通過網絡下載圖片,Picasso也可以載入本地圖片資源:
Picasso.with(context).load(R.drawable.icon).into(imageView);
Picasso.with(context).load("file:///android_asset/Adnroid.png").into(imageView);
Picasso.wiht(context).load(new File(...)).into(imageView);
7.調試:爲了方便調試,你可以通過調用Picasso的setIndicatiorEnabled(true);可以讓不同來源的圖片顯示一個不同的色彩標記;
以下轉自: http://blog.csdn.net/smallcheric/article/details/51055013
分析一下一些特殊情況下,Picasso的用法.
調用.noFade()
Picasso的默認圖片加載方式有一個淡入的效果,如果調用了noFade()
,加載的圖片將直接顯示在ImageView
上
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.future_studio_launcher)
.noFade()
.into(imageViewFade);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
調用.noPlaceholder()
有一個場景,當你從網上加載了一張圖片到Imageview
上,過了一段時間,想在同一個ImageView
上展示另一張圖片,這個時候你就會去調用Picasso,進行二次請求,這時Picasso就會把之前的圖片進行清除,可能展示的是.placeholder()
的圖片,給用戶並不是很好的體驗,如果調用了noPlaceholder()
,就不會出現這種情況.
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher)
.into(imageViewNoPlaceholder, new Callback() {
@Override
public void onSuccess() {
// 當上次加載完成後,進行二次加載
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[1])
.noPlaceholder()
.into(imageViewNoPlaceholder);
}
@Override
public void onError() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
調用resize(x, y)
來自定義圖片的加載大小
如果圖片很大或者想自定義圖片的顯示樣式,可以調用該API來解決這個問題;
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.resize(600, 200)
.into(imageViewResize);
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
調用`onlyScaleDown()來縮短圖片的加載計算時間
如果我們調用了resize(x,y)
方法的話,Picasso一般會重新計算以改變圖片的加載質量,比如一張小圖變成一張大圖進行展示的時候,但是如果我們的原圖是比我們從新resize的新圖規格大的時候,我們就可以調用onlyScaleDown()
來直接進行展示而不再重新計算.
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.resize(6000, 2000)
.onlyScaleDown() // 如果圖片規格大於6000*2000,將只會被resize
.into(imageViewResizeScaleDown);
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
對拉伸圖片的處理
如果圖片被操作了,可能在展示的時候就會比較醜,我們是想改變這種情景的,Picasso給我們提供了兩種選擇進行圖片展示,centerCrop()
或者centerInside()
.
-
centerCrop()
- 圖片會被剪切,但是圖片質量看着沒有什麼區別 -
Inside()
- 圖片會被完整的展示,可能圖片不會填充滿
ImageView`,也有可能會被拉伸或者擠壓
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.resize(600, 200)
.centerInside() 或者調用 .centerCrop()
.into(imageViewResizeCenterInside);
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
調用.fit()
來智能展示圖片
如果調用了該API, Picasso會對圖片的大小及ImageView
進行測量,計算出最佳的大小及最佳的圖片質量來進行圖片展示,減少內存,並對視圖沒有影響;
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.fit()
.into(imageViewHero);
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
調用.priority()
設置圖片加載的優先級
如果一個屏幕上頂部圖片較大,而底部圖片較小,因爲Picasso是異步加載,所以小圖會先加載出來,但是對於用戶來說,更希望看到的是上面的圖片先加載,底部的圖片後加載,Picasso支持設置優先級,分爲HIGH
, MEDIUM
,
和 LOW
,所有的加載默認優先級爲MEDIUM
;
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.fit()
.priority(Picasso.Priority.HIGH)
.into(imageViewHero);
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
注意:設置優先級並不能保證圖片就一定會被優先加載,只是會偏向傾斜於先加載;
調用tag()
爲請求添加標記提升用戶體驗
我們都知道,在一個ListView
的子item
中加載一張圖片是很常見的,這些圖片都來源於網絡請求,如果這個listview
有上千條數據,當用戶快速滑動的時候,每個item會不斷的被複用,當然Picasso的請求也不斷地進行請求,取消請求,再次請求,再次取消的操作(對屏幕外的自動取消請求),但是如果有一個方案,可以在用戶在快速滑動的時候全部停止請求,只有在滑動停止時再去請求,就非常完美了;
Picasso提供了三種設置Tag的方式
- 暫停標記
pauseTag()
- 可見標記
resumeTag()
- 取消標記
cancleTag()
pauseTag()
和 resumeTag()
的用法
在圖片請求時添加標記
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.tag("Profile ListView") //參數爲 Object
.into(imageViewWithTag);
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
然後讓listview
實現滑動監聽
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
final Picasso picasso = Picasso.with(context);
if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
picasso.resumeTag("Profile ListView");
} else {
picasso.pauseTag("Profile ListView");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
cancleTag()
的使用場景
試想一下,當你在瀏覽購物車的時候,這個時候就會去展示所有被選中item的圖片資源,如果這個時候用戶點擊了購買按鈕,就會彈出一個progressdialog
去請求數據以進行頁面跳轉,這個時候原來的請求就需要取消掉了;
public void buyButtonClick(View v) {
showDiaolg();
// 取消網絡請求
Picasso
.with(context)
.cancelTag("ShoppingCart");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
注意:如果tag狀態爲pause或者resume的話,Picasso會對tag持有一個引用,如果此時用戶退出了當前Activity
,垃圾回收機制進行回收的時候,就會出現內存泄露,所以需要在onDestory()
方法中進行相應處理;
.fetch()
, .get()
及 Target
之間的區別
.fetch()
- 該方法會在後臺異步加載一張圖片,但是不會展示在ImageView
上,也不會返回Bitmap
,這個方法只是爲了將獲取到的資源加載到本地和內存中,爲了後期加載縮短時間;.get()
- 該方法也是一個異步線程,不過加載完成後會返回一個Bitmap
,但是需要注意,該方法不能在主線程中調用,因爲會造成線程阻塞;Target
- 我們之前調用.into()
方法,只是將獲取到的資源加載到ImageView
中,但我們還可以將資源作爲回調放到Target
中,上代碼:
private Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
//加載成功後會得到一個bitmap,可以自定義操作
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
// 加載失敗進行相應處理
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.into(target);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
注意:你可以使用.get()
或者Target
獲取圖片的Bitmap
,但是當你使用Target
時,不能使用匿名內部類的方式,因爲垃圾回收機制在你獲取不到Bitmap
的時候會把對象回收;
Picasso在自定義Notifications
上的使用
Picasso有一個功能是可以加載圖片到RemoteViews
上,而RemoteViews
是用在Widgets
及自定義notification
佈局上的,下面通過一個小的示例來看Picasso是如何起作用的;
private void testRemoteView() {
RemoteViews remoteViews = new RemoteViews(getPackageName(),R.layout.item_picasso);
remoteViews.setImageViewResource(R.id.iv_remoteview,R.mipmap.abc);
remoteViews.setTextViewText(R.id.tv_title,"This Title");
remoteViews.setTextViewText(R.id.tv_desc,"This desc");
remoteViews.setTextColor(R.id.tv_title,getResources().getColor(android.R.color.black));
remoteViews.setTextColor(R.id.tv_desc,getResources().getColor(android.R.color.holo_blue_bright));
NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this)
.setSmallIcon(R.mipmap.notifation)
.setContentTitle("Context Title")
.setContentText("Content Text")
.setContent(remoteViews)
.setPriority(NotificationCompat.PRIORITY_MIN);
Notification notification = builder.build();
if (Build.VERSION.SDK_INT > 16){
notification.bigContentView = remoteViews;
}
NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(NOTIFICATION_ID,notification);
Picasso.with(MainActivity.this)
.load("http://www.jycoder.com/json/Image/3.jpg")
.into(remoteViews,R.id.iv_remoteview,NOTIFICATION_ID,notification);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
上面可以看到,Picasso的使用也是非常簡單,只需要調用.into()
的另一個重載方法即可: .into(Android.widget.RemoteViews
remoteViews, int viewId, int notificationId, android.app.Notification notification)
效果如下
demo需要提供提供開源庫picasso.jar
在Android.mk 文件增加:
LOCAL_STATIC_JAVA_LIBRARIES += picasso
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
picasso:libs/picasso-2.5.2.jar