1. 程式人生 > >Android開發之圖片載入快取框架Picasso的領教

Android開發之圖片載入快取框架Picasso的領教

Picasso實現了圖片的非同步載入,並解決了Android中載入圖片時常見的一些問題,它有以下特點:
  • 在Adapter中取消了不在檢視範圍內的ImageView的資源載入,因為可能會產生圖片錯位;
  • 使用複雜的圖片轉換技術降低記憶體的使用。
  • 自帶記憶體和硬碟的二級快取機制。
  • 內部也是整合LRU(近期最少使用)演算法。
Picasso優先判斷是否安裝有OkHttpClient網路引擎,沒有的話則採用常規的HttpUrlConnection進行網路請求,
static Downloader createDefaultDownloader(Context context) {
    try {
      Class.forName("com.squareup.okhttp.OkHttpClient");
      return OkHttpLoaderCreator.create(context);
    } catch (ClassNotFoundException ignored) {
    }
    return new UrlConnectionDownloader(context);
  }
Picasso預設將圖片存到/data/data/<application package>/cache/picasso-cache內部儲存空間裡,開闢的空間大小最小是5兆最大是50兆,同時採用Lru演算法來進行移除不常用的圖片減少記憶體空間佔有提高利用率。載入過程中預設開啟三個執行緒進行本地與網路之間的訪問。載入圖片的順序是 記憶體->本地->網路。 將Picasso新增到專案中: 目前Picasso的最新版本是2.5.2,你可以下載對應的Jar包,將Jar包新增到你的專案中,或者在build.gradle配置檔案中加入
compile 'com.squareup.picasso:picasso:2.5.2'
注意如果你開啟了混淆,你需要將以下程式碼新增到混淆規則檔案中:
-dontwarn com.squareup.okhttp.**
基本使用方法 Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
Picasso.with(context).load(R.drawable.landing_screen).into(imageView1); Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2);
Picasso.with(context).load(new File(...)).into(imageView3); 高階使用方法 1.新增載入中佔位圖和載入失敗佔位圖
Picasso .with(context)
               .load(imageUrl)
               .placeholder(R.mipmap.ic_launcher)              //新增佔位圖片
               .error(R.mipmap.ic_launcher)                          //載入失敗顯示的圖片
               .into(imageView);
也可判斷載入結果是否成功:
Picasso.with(this).load(userPhotoUrl).into(user_photo, new Callback() {
@Override
public void onSuccess() {
        //載入成功
    }

@Override
public void onError() {
        //載入失敗
    }
});

2.呼叫.noFade()Picasso的預設圖片載入方式有一個淡入的效果,如果呼叫了noFade(),載入的圖片將直接顯示在ImageView上

Picasso .with(context)
               .load(imageUrl)
               .placeholder(R.mipmap.ic_launcher)              //新增佔位圖片
               .error(R.mipmap.ic_launcher)                          //載入失敗顯示的圖片
               .noFade()                                                          // 去除淡入效果
               .into(imageView);

3.呼叫.noPlaceholder()。有一個場景,當你從網上載入了一張圖片到Imageview上,過了一段時間,想在同一個ImageView上展示另一張圖片,這個時候你就會去呼叫Picasso,進行二次請求,這時Picasso就會把之前的圖片進行清除,可能展示的是.placeholder()的圖片,如果你沒有設定佔位圖,則請求的過程中將會是一片空白,給使用者並不是很好的體驗,如果呼叫了noPlaceholder(),就不會出現這種情況.

Picasso .with(context)
               .load(imageUrl)
               .noPlaceholder(R.mipmap.ic_launcher)              //去除佔位圖
               .into(imageView);
4.呼叫resize(x, y)。自定義圖片的載入大小
Picasso .with(context)
               .load(imageUrl)
               .resize(600,200)             //自定義圖片的載入大小
               .into(imageView);

5.呼叫onlyScaleDown()。一般.resize(x,y)會將載入的圖片的重新計算然後按照設定展示出來。如果我們載入的圖片的尺寸比.resize(x,y)的尺寸還大,此時呼叫.onlyScaleDown()將會直接將圖片按照.resize(x,y)的尺寸展示出來,效率提高了圖片的載入時間。

Picasso .with(context)
               .load(imageUrl)
               .resize(600,200)             //自定義圖片的載入大小
               .onlyScaleDown()         //效率提高圖片的載入時間配合resize(x,y)使用。           
               .into(imageView);

6.針對拉伸圖片的處理。Picasso給我們提供了兩種進行圖片拉伸的展示方式,視情況選擇,避免圖片拉伸變形難看。

centerCrop() - 圖片會根據最短邊進行多餘剪下,但是圖片質量看著沒有什麼區別。

Inside()- 圖片會被完整的展示,圖片過小使圖片不會填充滿ImageView`,圖片過大則會硬填充到view中,圖片此時出現變形。
Picasso .with(context)
               .load(imageUrl)
               .resize(600,200)             //自定義圖片的載入大小
               .centerCrop()               //填充居中裁剪顯示
               .into(imageView);

7.呼叫.fit()。Picasso會對圖片的大小及ImageView進行測量,計算出最佳的大小及最佳的圖片質量來進行圖片展示,減少記憶體,並對檢視沒有影響;

Picasso .with(context)
               .load(imageUrl)
               .fit()
               .into(imageView);

8.呼叫.priority()。設定圖片載入的優先順序。通常情況我們進行網路載入圖片時一般是哪個圖片小就先把那個圖片先加載出來。有些時候我們想讓圖片按照某個順序或者等級載入圖片,Picasso為我們提供了三種圖片載入的優先順序:HIGH, MEDIUM,  LOW。所有的載入預設優先順序為MEDIUM。

Picasso .with(context)
               .load(imageUrl)
               .fit()
               .Priority(Picasso.Priority.HIGH)    //設定圖片載入的優先順序
               .into(imageView);

9.呼叫.tag()。控制圖片的載入暫停或取消。

Picasso提供了三種設定Tag的方式 暫停標記 pauseTag() 可見標記 resumeTag() 取消標記 cancleTag()
Picasso .with(context)
               .load(imageUrl)
               .tag("PICASSO_TAG")
               .into(imageView);
針對listView(),有些時候我們想在滑動的時候不載入圖片,滑動結束的時候在進行載入我們可以重寫下邊的這個方法。
Picasso .with(context)
               .load(imageUrl)
               .tag("PICASSO_TAG")
               .into(imageView);
有些時候我們針對當前的請求需要中斷:
Picasso .with(context)
              .cancelTag("PICASSO_TAG");
10.指定Target。我們之前呼叫.into()方法,只是將獲取到的資源載入到ImageView中,但我們還可以將資源作為回撥放到Target中。但是這裡的target不能作為匿名內部類直接引用,即不能在.into()裡直接new出來,因為垃圾回收機制在你獲取不到Bitmap的時候會把物件回收。 Picasso下載圖片
private void download() {
        //獲得圖片的地址
        String url = mList.get(mPosition);
        //Target
        Target target = new Target(){
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                String imageName = System.currentTimeMillis() + ".png";
                File dcimFile = FileUtil
                        .getDCIMFile(FileUtil.PATH_PHOTOGRAPH,imageName);        
                FileOutputStream ostream = null;
                try {
                    ostream = new FileOutputStream(dcimFile);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, ostream);
                    ostream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                Toast.makeText(PicActivity.this,"圖片下載至:"+dcimFile,Toast.LENGTH_SHORT).show();
            }
            @Override
            public void onBitmapFailed(Drawable errorDrawable) {
            }
            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {
            }
        };
        //Picasso下載
        Picasso.with(this).load(url).into(target);
    }
11.自定義通知欄採用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);
}

12.圖片旋轉

a.根據自身中心旋轉:
Picasso .with(context)
        .load(imageUrl)
        .rotate(90f) //旋轉90度
.into(imageView);
b.根據某個點旋轉:
Picasso .with(context)
        .load(imageUrl)
        .rotate(45f, 200f, 100f)    //以中心點(200,100)旋轉45度。
.into(imageView);

13.Picasso對圖片的轉化(Transformation擴充套件)。

先了解一下Transformation這個類,這是一個介面,裡面有兩個方法,其中transForm這個方法最重要,負責對Bitmap的轉換操作。
publicinterface Transformation {
    //對Bitmap進行轉換操作
  Bitmap transform(Bitmap source);
    //一個key  主要是快取的key的生成和它有關
  String key();
}

a.將載入完成的圖片做成圓形:

public class CircleTransform implements 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;

//建立一個正方形區域的Btimap
Bitmap squaredBitmap=Bitmap.createBitmap(source,x,y,size,size);

if(squaredBitmap!=source){
            source.recycle();
        }

//建立一張可以操作的正方形圖片的點陣圖
Bitmap bitmap=Bitmap.createBitmap(size,size,source.getConfig());

//建立一個畫布Canvas
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(context)
        .load(UsageExampleListViewAdapter.eatFoodyImages[0])
        .transform(new CircleTransform())
        .into(imageViewTransformationBlur);
b.模糊一張圖片
public class BlurTransformation implements Transformation {

    RenderScript rs;

public BlurTransformation(Context context) {
super();
rs = RenderScript.create(context);
    }

@Override
public Bitmap transform(Bitmap bitmap) {
// 建立一個Bitmap作為最後處理的效果Bitmap
Bitmap blurredBitmap = bitmap.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);

// 將結果copy到blurredBitmap中
output.copyTo(blurredBitmap);

//釋放資源
bitmap.recycle();

return blurredBitmap;
    }

@Override
public String key() {
return "blur";
    }
}
呼叫:
Picasso.with(context)
        .load(UsageExampleListViewAdapter.eatFoodyImages[0])
        .transform(new BlurTransformation(Context))
        .into(imageViewTransformationBlur);
c.實現圓形模糊圖片:
Picasso.with(context)
        .load(UsageExampleListViewAdapter.eatFoodyImages[0])
        .transform(new BlurTransformation(Context))
        .into(imageViewTransformationBlur);

14.呼叫.memoryPolicy()。

memoryPolicy主要用於Picasso對記憶體的管理。有兩個列舉類: NO_CACHE - 讓Picasso跳過從記憶體中讀取圖片這一操作 NO_STORE - 如果你的圖片只加載一次就沒用了,就呼叫該值,這樣的話Picasso就不會在記憶體及本地進行快取了。
Picasso  
    .with(context)
    .load(url)
    .memoryPolicy(MemoryPolicy.NO_CACHE)
    .into(imageViewFromDisk);
Picasso  
    .with(context)
    .load(url)
    .memoryPolicy(MemoryPolicy.NO_CACHE,MemoryPolicy.NO_STORE)
    .into(imageViewFromDisk);

15.呼叫.networkPolicy()。

networkPolicy和memoryPolicy類似,前者是對記憶體管理後者是對儲存空間管理。有三個列舉類:
NO_CACHE - 讓Picasso跳過從本地讀取資源這一過程 NO_STORE - 讓Picasso不進行本地圖片快取 OFFLINE - 讓Picasso載入圖片的時候只從本地讀取除非聯網正常並且本地找不到資源的情況下進行網路請求。
Picasso  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[2])
    .networkPolicy(NetworkPolicy.NO_CACHE)
    .into(imageViewFromNetwork);
或配合memoryPolicy一起使用:
Picasso  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[2])
    .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
    .networkPolicy(NetworkPolicy.NO_CACHE)
    .into(imageViewFromNetwork); 

16.呼叫.setIndicatorsEnabled(true),檢視圖片來源。

興許載入圖片的時候我們不知道載入的圖片是從記憶體加載出來還是從本地加載出來的還是直接從網路上加載出來的,這時候我們呼叫.setIndicatorsEnabled(true)會在圖片的左上角出現一個有顏色的小三角幫助我們區分,總共有三種顏色: 藍色 - 從記憶體中獲取,是最佳效能展示 綠色 - 從本地獲取,效能一般 紅色 - 從網路載入,效能最差
Picasso  
    .with(context)
    .setIndicatorsEnabled(true);
然後在進行載入圖片就可以看到每張圖片的來源了
Picasso  
    .with(context)
    .load(url)
    .into(imageViewFromDisk);

17.呼叫.setLoggingEnabled(true)。用來檢視每張圖片加載出來的用時多少,日誌在控制檯裡打出來。

Picasso  
    .with(context)
    .setLoggingEngabled(true);
然後再進行載入圖片就可以看到每張圖片的日誌了
Picasso  
    .with(context)
    .load(url)
    .into(imageViewFromDisk);

18.整體載入分析StatsSnapshot。有些時候我們想看我們載入了這麼多張圖片後圖片記憶體佔用多少,可以採用StatsSnapshot.

StatsSnapshot picassoStats = Picasso.with(context).getSnapshot();
然後列印日誌:
Log.d("Picasso Stats", picassoStats.toString());