一,簡介:
1簡意:picasso是Square公司開源的一個Android圖形緩存庫,地址http://square.github.io.picasso/
2.突出優點:可以實現圖片下載和緩存功能
在實際開發中異步圖片加載時需考慮:
(1)在adapter中需要取消已經不在視野範圍的ImageView圖片資源的加載,否則會導致圖片錯位,Picasso已經解決了這個問題
(2)複雜的圖片進行壓縮,儘量減少內存的消耗
(3)實現內存緩存和二級硬盤緩存的效果
3.使用Picasso框架解決的圖片下載中遇到的問題:
(1)在adapter中回收和取消當前下載
(2)使用最少的內存完成圖片的轉換
(3)自帶內存和硬盤緩存
(4)圖形轉換,比如大小、旋轉等
(5)加載網絡資源和本地資源
(6)採用鏈式調用
4.與Universal-ImageLoader庫對比:
1.都有高效的網絡圖片下載和緩存性能;
2.Universal-ImageLoader功能多,靈活使用配置;
3.Picasso使用複雜的圖片壓縮轉換來儘可能的減少內存消耗;
4.在Adapter中需要取消已經不在視野範圍的ImageView圖片資源的加載,否則會導致圖片錯位,Picasso已經解決了這個
二,簡單使用
1.加載一張圖片:
(1)添加依賴:點擊app右鍵---》open module settings -->Dependencies下---右上角+號-->dependecy,輸入picasso,選擇com.squareup.picasso.Picasso
(2)使用,如,爲按鈕設置監聽,imageview顯示圖片
佈局:定義一個button,一個ImageView
代碼:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Picasso.with(MainActivity.this).load(IMAGE_PATH).placeholder(R.mipmap.ic_launcher).fit().into(mImageView);
}
});
private final String IMAGE_PATH = "http://www.leawo.cn/attachment/201404/15/1077476_1397532112ke37.png";
效果:(添加網絡權限)點擊按鈕後顯示一張網絡圖片(但是在顯示之前會有一個短暫的顯示本地圖片的過程)
2.用listView來顯示url請求回來的數據:新聞圖片+內容
(1)佈局,定義ListView,在Activity中爲該listView綁定控件id,
(2)自定義Picasso類:提供幾個方法,如下:
public class PicassoUtils {
/**
* 指定圖片寬高進行加載
* @param context
* @param path
* @param width
* @param height
* @param imageView
*/
public static void loadImageWithSize(Context context , String path , int width ,
int height , ImageView imageView){
Picasso.with(context).load(path).resize(width , height).centerCrop().into(imageView);
}
/**
* 根據resID來加載圖片
* @param context
* @param path
* @param resID
* @param imageView
*/
public static void loadImageWithHolder(Context context , String path , int resID , ImageView imageView){
Picasso.with(context).load(path).placeholder(resID).into(imageView);
}
/**
* 自定義裁剪類來加載圖片
* @param context
* @param path
* @param imageView
*/
public static void loadImageWithCrop(Context context , String path , ImageView imageView){
Picasso.with(context).load(path).transform(new CropSquareTransformation()).into(imageView);
}
/**
* 實現對圖片的自定義裁剪
*/
public static class CropSquareTransformation 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;
Bitmap result = Bitmap.createBitmap(source , x ,y ,size , size);
if(result != null){
source.recycle();//進行回收
}
return result; //返回圖片
}
@Override
public String key() {
return "square()"; //自己取一個名字
}
}
}
(3)根據url,查看返回結果,創建實體類,這裏取圖片,summary,subject:
url:本來
http://litchiapi.jstv.com/api/GetFeeds?column=17&PageSize=20&pageIndex=1&val=AD908EDAB9C3ED111A58AF86542CCF50
bean類:
public class NewsItem {
private String subject ; //新聞主題
private String summary ; //新聞內容
private String cover ; //新聞圖片
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getCover() {
return cover;
}
public void setCover(String cover) {
this.cover = cover;
}
}
(4)創建訪問任務,這裏先用AsyncTask:代碼如下:
/**
* 異步任務實現:String,void,返回類型:一個list<NewsItem>集合
*/
class MyTask extends AsyncTask<String , Void , List<NewsItem>>{
/**
* 準備執行前:顯示窗口進度條
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
//進度條窗口顯示
dialog.show();
}
@Override
protected List<NewsItem> doInBackground(String... params) {
//創建HttpClient對象
HttpClient httpClient = new DefaultHttpClient();
//創建HttpGet
HttpGet httpGet = new HttpGet(params[0]);
//創建返回對象
HttpResponse response = null;
//創建List<NewsItem>集合
List<NewsItem> list = new ArrayList<>();
try {
//獲取返回結果對象Response
response = httpClient.execute(httpGet);
//判斷是否請求成功
if(response.getStatusLine().getStatusCode() == 200){
//獲取HttpEntity對象
HttpEntity entity = response.getEntity();
String json_value = EntityUtils.toString(entity , "utf-8");
//根據返回數據進行解析:
//http://litchiapi.jstv.com/api/GetFeeds?column=17&PageSize=20&pageIndex=1&val=AD908EDAB9C3ED111A58AF86542CCF50
JSONArray jsonArray = new JSONObject(json_value)
.getJSONObject("paramz").getJSONArray("feeds");
//獲取每一條數據
for(int i = 0; i < jsonArray.length(); i++){
JSONObject element = jsonArray.getJSONObject(i).getJSONObject("data");
NewsItem item = new NewsItem();
item.setCover(element.getString("cover"));
item.setSubject(element.getString("subject"));
item.setSummary(element.getString("summary"));
list.add(item);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
@Override
protected void onPostExecute(List<NewsItem> newsItems) {
super.onPostExecute(newsItems);
//完成時初始化MyAdapter
adapter = new MyAdapter(newsItems);
//爲listView設置MyAdapter
listView.setAdapter(adapter);
//進度條窗口取消
dialog.cancel();
}
}
其中窗口進度條:
//定義進度條窗口
private ProgressDialog dialog ;
//創建進度條窗口對象
dialog = new ProgressDialog(this);
//設置窗口標題
dialog.setTitle("loading......");
(5)自定義MyAdapter繼承BaseAdapter:
/**
* 自定義適配器
*/
class MyAdapter extends BaseAdapter{
//List<NewsItem>集合
private List<NewsItem> data;
/**
* 初始化適配器
* @param data
*/
public MyAdapter(List<NewsItem> data){
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//創建MyViewHolder
MyViewHolder holder = null;
//一次
if(convertView == null){
holder = new MyViewHolder();
convertView = getLayoutInflater().inflate(R.layout.my_adapter_layout , parent , false);
holder.cover = (ImageView)convertView.findViewById(R.id.cover_img);
holder.subject = (TextView)convertView.findViewById(R.id.subject);
holder.summary = (TextView)convertView.findViewById(R.id.summary);
convertView.setTag(holder);
Log.d(TAG , "convertView:" +position);
}else {
holder = (MyViewHolder)convertView.getTag();
}
//分別設置圖片,主題,內容
holder.subject.setText(data.get(position).getSubject());
//Log.d(TAG , data.get(position).getSubject());
holder.summary.setText(data.get(position).getSummary());
//Log.d(TAG , data.get(position).getSummary());
PicassoUtils.loadImageWithSize(ListViewActivity.this , "http://litchiapi.jstv.com" + data.get(position).getCover(),
400,300,holder.cover);
return convertView;
}
}
MyViewHolder:
private static class MyViewHolder{
ImageView cover;
TextView subject;
TextView summary;
}
MyAdapter的佈局:就是一個圖片,兩個textView(對應新聞圖片,標題,內容)
記得添加網絡權限:
<uses-permission android:name="android.permission.INTERNET"/>
(6)如果需要解決用戶飛速下滑(即不想瀏覽中間某些新聞)時,picasso可以解決:
自定義onScrollListener類:
/**
* 自定義OnScrollListener類
*/
public class ListScroller implements AbsListView.OnScrollListener{
/**
* 下滑狀態改變時調用
* @param view
* @param scrollState
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//創建Picasso
final Picasso picasso = Picasso.with(ListViewActivity.this);
//判斷狀態
if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL){
//更新
picasso.resumeTag(ListViewActivity.this);
}else {
//暫停
picasso.pauseTag(ListViewActivity.this);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
}
爲listView設置滑動監聽:
//listView.setOnScrollListener(new ListScroller());
即可以,截圖略附註代碼地址:
https://github.com/maiyu-green/PicassoDemo
三,picasso的特點與常用設置方法:
通過以上的簡單介紹,我們大概知道:
1.Picasso的特點:
總結以上,picasso的特點:
(1)自帶內存和硬盤二級緩存功能
(2)加載本地資源,資產,SD卡及ContentProvider中的圖片
(3)在Adapter中取消已經不在視野範圍的ImageView圖片資源的加載,否則會導致圖片錯位,Picasso解決了圖片錯位問題
(4)使用圖片壓縮儘可能的減少內存消耗
(5)圖形轉換操作,如變換大小,旋轉等,提供了接口來讓用戶自定義轉換操作
2.Picasso的常用設置方法
(1)加載本地資源、資產目錄、SD卡已經ContentProvider的圖像
基本語法:
Picasso.with(上下文).load(圖片).into(ImageView)
加載圖片資源:
//加載資源圖片
Picasso.with(this).load(R.mipmap.ic_launcher).into(mImageView);
//加載資產目錄圖片
Picasso.with(this).load("file://android_asset/logo.png").into(mImageView);
//加載SD Card圖片文件
Picasso.with(this).load(new File("路徑")).into(mImageView);
//加載網絡圖片
Picasso.with(this).load("url地址").into(mImageView);
(2)設置佔位圖片:下載前,下載出錯時的圖像,picasso會嘗試3次請求,三次都失敗會顯示error方法:
Picasso.with(context)
.load(url)
//下載前顯示圖片
.placeholder(R.mipmap.ic_launcher)
//下載出錯顯示圖片
.error(R.mipmap.ic_launcher)
.into(mImageView);
(3)無淡入淡出:
.noFade()
(4)圖片重新調整大小:有兩個方法
.resize(width,height)
.resizeDimen(targetWidthResId , targetHieghtResId)
(5)圖片剪切類型:centerCrop,fit,centerInside
放在load之後:centerCrop與centerInside需要配合resize(),放在它之後,而fit不能再resize(),
centerCrop----會根據resize()方法的寬高進行裁剪
centerInside---會根據resize()方法的寬高進行比例縮放
如一張長方形圖片,resize(100,100)後,centerCrop---結果正方形,centerInside結果--長方形縮小圖
(6)自定義圖形轉換:爲了更好的適配佈局和減少存儲空間,
利用transform方法:
/**
* 實現對圖片的自定義裁剪
*/
public static class CropSquareTransformation 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;
Bitmap result = Bitmap.createBitmap(source , x ,y ,size , size);
if(result != null){
source.recycle();//進行回收
}
return result; //返回圖片
}
@Override
public String key() {
return "square()"; //自己取一個名字
}
}
(7)圖片旋轉:
.rotate()方法
(8)設置圖片質量對於不透明的圖片可以使用RGB_565(一個像素點佔用2個字節)來優化內存。
默認情況下Android使用ARGB_8888---一個像素點佔用4個字節
用肉眼看,效果差不多
.config(Bitmap.Config.GRB_566)
(9)查看大圖時放棄內存緩存memory cache:
Picasso默認會使用設備的15%的內存作爲內存圖片緩存,且現有的API無法清空內存緩存
在查看大圖時放棄使用內存緩存,圖片從網絡下載完成後會自動緩存到磁盤中,加載會從磁盤中加載,這樣可以加速內存回收
Picasso.with(getApplicationContext())
.load(url)
//NO_CACHE指圖片加載跳過從內存緩存查找,NO_STORE指圖片存儲時不往內存緩存中存儲
.memoryPolicy(MemoryPolicy.NO_CACHE , MemoryPolicy.NO_STORE)
.into(mImageView);
(10)設置tag標籤:可以用來pause,resume和cancel訪問
Picasso.with(this).pauseTag("pause標籤");
Picasso.with(this).resumeTag("resume標籤");
Picasso.with(this).cancelTag("標籤");
以下是在manifest中的設置:
(11)新進程中查看大圖:
列表頁的內存已經非常穩定,但查看大圖時,常佔用20多兆內存,加上現有進程內存,容易OOM
所以,在新進程中打開Activity成爲比較巧取的避免oom的方式
<activity android:name=".MainActivity"
android:process=":picture"/>
只要在定義activity是添加process屬性,即可在新進程中打開此activity,因爲Picasso也將在新進程中創建基於新ApplicationContext的單例3.Picasso在Application的常見設置
private void intoPicasso(){
Picasso picasso = new Picasso.Builder(this)
//設置內存緩存大小 10m
.memoryCache(new LruCache(10 << 20))
//下載圖片的格式,採用rgb_565節省一半內存
.defaultBitmapConfig(Bitmap.Config.RGB_565)
//配置下載器
//.downloader(new UrlConnectionDownloader())
//.downloader(new OkHttpDownloader())
.downloader(new MyDownLoader(getCacheDir() , 20 << 20))
//設置調試標誌,位於左上角
//紅色---代表從網絡下載的圖片
//藍色--代表從磁盤緩存加載的圖片
//綠色--代表從內存中加載的圖片
.indicatorsEnabled(true)
.build();
Picasso.setSingletonInstance(picasso);
}