图片开源框架之Picasso

介绍

 picasso是Square公司开源的一个Android图形缓存库,地址:http://square.github.io/picasso/ 它不仅满足加载图片的基本需求,而且扩展性也很强

版本和大小

 目前最新版本为:2.5.2,对应jar包名称:picasso-2.5.2.jar
 jar包大小124K

Picasso功能

 1.加载速度快
 2.资源消耗低
 3.保证加载图片时不错位
  4.加载图片类型丰富,支持网络图片以及各种本地图片如Asserts目录下,内容提供者提供的图片资源路径

Picasso策略

  1.加载速度快
  标准的二级缓存(内存缓存+磁盘缓存)+Net
  a.标配策略,MemoryCache+DiskCache+Net。提高加载速度,同时保证流量。
  b.Net部分,兼顾单请求加载速度与多请求并发能力,从而提高整体加载速度。
c.MemoryCache部分,通过Lru策略提高缓存效率。
  2.资源消耗低
  a.渲染适当尺寸图片来减少内存。
  b.通过线程池来限制并发的图片加载线程,降低资源消耗。
  c.请求相同图片的线程要合并,减少线程数。
  3.加载图片类型丰富
  Picasso内置针对不同的图片资源类型的各种handler 例如:网络下载图片:NetworkRequestHandler,Asserts目录下的图片资源:AssetRequestHandler
 4.保证加载图片时不错位
  Picasso维护了Map,Key为ImageView,Value为Action,每个ImageView均只对应一个Action。若获取的图片Action与ImageView不符合,则丢弃,等待正确的Action执行完。
 

Picasso所需要的权限

如下:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

picasso嵌入工程方式

本地依赖

1.将picasso-2.5.2.jar拷贝到工程libs目录中
2.改写工程build.gradle如下

 apply plugin: 'com.android.application'
  android {
     compileSdkVersion 23
     buildToolsVersion "23.0.1"

     defaultConfig {
         applicationId "com.meizu.analysepicasso"
         minSdkVersion 15
         targetSdkVersion 23
         versionCode 1
         versionName "1.0"
     }
     buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
     }
  }
  repositories {
    flatDir {
        dirs 'src/main/libs'
    }
  }

  dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile files('src/main/libs/picasso-2.5.2.jar')
  }
远程依赖

1.直接依赖远程仓库的picasso-2.5.2.jar,下载好的文件缓存在~/.gradle/caches目录中
2.改写工程build.gradle如下

apply plugin: 'com.android.application'
  android {
      compileSdkVersion 23
      buildToolsVersion "23.0.1"

      defaultConfig {
          applicationId "com.meizu.analysepicasso"
          minSdkVersion 15
          targetSdkVersion 23
          versionCode 1
          versionName "1.0"
      } 
      buildTypes {
          release {
              minifyEnabled false
              proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
          }
      }
  }
  dependencies {
      compile fileTree(dir: 'libs', include: ['*.jar'])
      testCompile 'junit:junit:4.12'
      compile 'com.android.support:appcompat-v7:23.1.1'
      compile 'com.squareup.picasso:picasso:2.5.2'
      compile 'com.squareup.okhttp:okhttp:2.+'
      compile 'com.squareup.okhttp:okhttp-urlconnection:2.+'
  }

picasso使用

链式编程
Picasso.with(MainActivity.this).load("https://www.baidu.com/img/bd_logo1.png").into(item.img_iv);

上述代码是不是看起来很直观,Picasso使用起来就是这么简单

列表项中使用

如何在具有缓存view功能,类似ListView,GridView中使用。
实例代码如下
首先是界面布局 activity_main_picasso.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >
    <ListView
        android:id="@+id/listview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:dividerHeight="1dip"
        />
</RelativeLayout>

界面中子元素的布局 main_listview_item:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_gravity="center_vertical"
    android:padding="5dip" >
    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="60dip"
        android:layout_height="60dip"
        android:scaleType="centerInside"
        android:src="@drawable/ic_launcher" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_marginLeft="15dip"
         >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" 
            android:textSize="20sp"
            android:layout_marginTop="5dip"/>

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" 
            android:textSize="14sp"
            android:layout_marginTop="2dip"/>
    </LinearLayout>
</LinearLayout>

处理逻辑代码:

public class MainActivity extends Activity {
  private static final String BASE_URL = "http://img1.3lian.com/img2011/w1/106/85/";
  private DrawLineTransformation drawLineTransformation;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_picasso);
    ArrayList<Dish> dishList = new ArrayList<Dish>();
    dishList.add(new Dish(BASE_URL + "42.jpg", "水煮鱼片", "38.00"));
    dishList.add(new Dish(BASE_URL + "34.jpg", "小炒肉", "18.00"));
    dishList.add(new Dish(BASE_URL + "37.jpg", "清炒时蔬", "15.00"));
    dishList.add(new Dish(BASE_URL + "12.jpg", "金牌烤鸭", "36.00"));
    dishList.add(new Dish(BASE_URL + "13.jpg", "小鸡蹲蘑菇", "40.00"));
    dishList.add(new Dish(BASE_URL + "14.jpg", "麻婆豆腐", "25.00"));
    dishList.add(new Dish(BASE_URL + "15.jpg", "北京烧鸭", "30.00"));
    dishList.add(new Dish(BASE_URL + "16.jpg", "麻辣风爪", "25.00"));
    dishList.add(new Dish(BASE_URL + "17.jpg", "宫爆鸡丁", "36.00"));
    dishList.add(new Dish(BASE_URL + "19.jpg", "椒盐虾", "45.00"));
    dishList.add(new Dish(BASE_URL + "20.jpg", "清蒸鲈鱼", "20.00"));
    MainListViewAdapter adapter = new MainListViewAdapter(dishList);
    mListView.setAdapter(adapter);
    }
  private class MainListViewAdapter extends BaseAdapter {
    private ArrayList<Dish> dishList;
    public MainListViewAdapter(ArrayList<Dish> list) {
    this.dishList = list;
    }
    @Override
    public int getCount() {
    return dishList.size();
    }
    @Override
    public Object getItem(int position) {
    return dishList.get(position);
    }
    @Override
    public long getItemId(int position) {
    return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    ListViewItemHolder item = null;
    if (convertView == null) {
           convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.main_listview_item, null);
       item = new ListViewItemHolder();
       item.img_iv = (ImageView) convertView.findViewById(R.id.imageView1);
           item.name_textview = (TextView) convertView.findViewById(R.id.textView1);
       item.price_textview = (TextView) convertView.findViewById(R.id.textView2);
       convertView.setTag(item);
    } else {
       item = (ListViewItemHolder) convertView.getTag();
        }
        Dish dish = dishList.get(position);
        Picasso.with(MainActivity.this).load(dish.getImgUrl()).into(item.img_iv);           
        item.name_textview.setText(dish.getName());
        item.price_textview.setText(dish.getPrice() + "元");
        return convertView;
    }
  }
  private class ListViewItemHolder{
       ImageView img_iv;
       TextView name_textview;
       TextView price_textview;     
  }
}

Tips:这里我们注意的是图片错位问题,像原来我们一般要对这种情况做特殊处理,一般使用的是利用url做为View的tag标签,
当要显示数据时,我们会对当前url和tag标签进行判断,当一样时才会显示.但是Piscasso已经帮我们进行了处理。

3.效果图如下:
这里写图片描述

占位符图片的使用

picasso支持请求图片之前显示一个图片和下载失败后显示错误图片
实例

Picasso.with(MainActivity.this).load(dish.getImgUrl()).placeholder(R.drawable.test).error(R.drawable.test).into(item.img_iv);
picasso调试

Picasso调试非常简单,只需要使用如下代码将其开关打开。

 Picasso.with(MainActivity.this).setIndicatorsEnabled(true);

Tips: 打开调试开关后,会在每个图片针对不同的加载显示不同颜色的三角形
现象解释:
红色:代表从网络下载的图片
蓝色:代表从磁盘缓存加载的图片
绿色:代表从内存中加载的图片

picasso释放内存

在OnDestroy方法中调用shutdown(),如下代码

Picasso.with(MainActivity.this).shutdown();

或者在适当时机通过反射释放内存,如下代码

 protected void clearPicassoCache() {
        Picasso picasso = Picasso.with(context.getApplicationContext());
        try {
            Object object = ReflectHelper.getField(picasso, "cache");
            ReflectHelper.invoke(object, "clear", null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
picasso降低内存使用

 Picasso默认使用的色彩模式时ARGB_8888,这就意味着一个像素会使用4个字节的空间大小,且不管imageview尺寸大小如何Picasso只缓存一个全尺寸的图片,这就会导致picasso使用内存相对较高,当我们对图片质量不是特别严谨时,或者对速度稍微可以容忍时,我们可以使用如下两种方式降低Picasso内存使用
第一种

Picasso.with(this)
    .load("https://www.baidu.com/img/bd_logo1.png");
    .resize(768, 432)
    .into(item.img_iv);

第二种

Picasso.with(this)
    .load("https://www.baidu.com/img/bd_logo1.png")
    .fit()
    .centerCrop()
    .into(item.img_iv);
picasso性能

 Picasso的线程池是优化过的,根据当前设备网络状况设置ThreadCount。
在网络良好的条件下,线程池持有较多线程,保证下载速度够快。在网络较差的条件下(2G网络等),线程池减少持有线程,保证带宽不会被多个连接阻塞。
  Picasso将图片uri、resize、transform等参数糅合为key,将key封装到Action中进行请求。请求线程Hunter对相同key的Action进行合并,请求完成后,Action依次得到图片。

picasso架构图

这里写图片描述

picasso时序图

这里写图片描述

picasso扩展
后续图片处理

比如说我们现在有一个这样需求,针对新商品,我们想在新商品左上角了加一个新的标签,当然这里也是有两种思路的:第一种:直接提供已经做好标签的图片,第二种直接对照片进行
处理,然后自己绘制上去,这里我们主要看下第二种,看如何扩展Picasso实现这个功能
Picasso提供接口类Transformation,其代码如下:

public interface Transformation {
  /**
   * Transform the source bitmap into a new bitmap. If you create a new bitmap instance, you must
   * call {@link android.graphics.Bitmap#recycle()} on {@code source}. You may return the original
   * if no transformation is required.
   */
  Bitmap transform(Bitmap source);

  /**
   * Returns a unique key for the transformation, used for caching purposes. If the transformation
   * has parameters (e.g. size, scale factor, etc) then these should be part of the key.
   */
  String key();
}

实例
  1.自定义的对图片处理类”’

class DrawLineTransformation implements Transformation {
   @Override
   public String key() {
     // TODO Auto-generated method stub
     return "drawline";
   }
   @Override
   public Bitmap transform(Bitmap bitmap) {
    // TODO Auto-generated method stub
    synchronized (DrawLineTransformation.class) {
    if(bitmap == null) {
       return null;
    }
    Bitmap resultBitmap = bitmap.copy(bitmap.getConfig(), true);
    Canvas canvas = new Canvas(resultBitmap);
    Paint paint = new Paint();
    canvas.save();
    paint.setColor(Color.RED);
    paint.setStyle(Paint.Style.FILL);
    canvas.drawRect(0, 0, resultBitmap.getWidth() / 3, resultBitmap.getHeight() / 3, paint);
    canvas.restore();
    paint.setTextAlign(Paint.Align.CENTER);
    String TEXT = "推荐";
    canvas.save();
    int canvasWidth = canvas.getWidth();
    int canvasHeight = canvas.getHeight();
    paint.setTextSize(30);
    paint.setStrokeWidth(10);
    float textHeight = paint.descent() - paint.ascent();
    int baseX = canvasWidth / 6;
    int baseY = canvasHeight / 6;
    paint.setColor(getResources().getColor(android.R.color.white));
    canvas.translate(0, (canvasHeight/6 - textHeight / 2));
    canvas.drawText(TEXT, baseX , baseY, paint);
    canvas.restore();
    bitmap.recycle();
    return resultBitmap;
     }
}

 2.如何使用自定义的对图片处理类,代码如下

Picasso.with(MainActivity.this).load(dish.getImgUrl()).transform(drawLineTransformation).into(item.img_iv); 
显示图片控件的扩展

是不是Picasso就只能通过ImageView来显示图片呢?答案当然是no,下面就让我们看看如何使用其他控件来显示图片。
  Picasso提供接口类Target,其代码如下:

 public interface Target {
  /**
   * Callback when an image has been successfully loaded.
   * <p>
   * <strong>Note:</strong> You must not recycle the bitmap.
   */
  void onBitmapLoaded(Bitmap bitmap, LoadedFrom from);

  /**
   * Callback indicating the image could not be successfully loaded.
   * <p>
   * <strong>Note:</strong> The passed {@link Drawable} may be {@code null} if none has been
   * specified via {@link RequestCreator#error(android.graphics.drawable.Drawable)}
   * or {@link RequestCreator#error(int)}.
   */
  void onBitmapFailed(Drawable errorDrawable);

  /**
   * Callback invoked right before your request is submitted.
   * <p>
   * <strong>Note:</strong> The passed {@link Drawable} may be {@code null} if none has been
   * specified via {@link RequestCreator#placeholder(android.graphics.drawable.Drawable)}
   * or {@link RequestCreator#placeholder(int)}.
   */
  void onPrepareLoad(Drawable placeHolderDrawable);
}   

实例
1.自定义的对图片显示类

public class ProfileView extends FrameLayout implements Target {
    public ProfileView(Context context) {
        super(context);
    }

    public ProfileView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        setBackgroundDrawable(new BitmapDrawable(bitmap));
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
        setBackgroundDrawable(errorDrawable);
    }

    @Override public void onPrepareLoad(Drawable placeHolderDrawable) {
        setBackgroundDrawable(placeHolderDrawable);
    }
}

2.如何使用自定义的对图片显示类,代码如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_gravity="center_vertical"
    android:padding="5dip" >
    <com.meizu.analysepicasso.ProfileView
    android:id="@+id/imageView1"
    android:layout_width="60dip"
    android:layout_height="60dip"
    android:scaleType="centerInside"
    android:src="@drawable/ic_launcher" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_marginLeft="15dip"
         >
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" 
            android:textSize="20sp"
            android:layout_marginTop="5dip"/>
        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" 
            android:textSize="14sp"
            android:layout_marginTop="2dip"/>
    </LinearLayout>
</LinearLayout>
Picasso如何集成其他优秀库

 Picasso不能能使用自己一套的下载器,当然也能天衣无缝的集成优秀框架OKHttp
实例
 1.工程依赖OKHttp库

 dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.squareup.picasso:picasso:2.5.2'
    compile 'com.squareup.okhttp:okhttp:2.+'
    compile 'com.squareup.okhttp:okhttp-urlconnection:2.+'
}

 2.Picasso中如何使用OKHttp库

public class PicassoToOkHttpActivity extends Activity {
  private static final String BASE_URL = "http://img1.3lian.com/img2011/w1/106/85/";
  private Picasso mPicasso;
  private DrawLineTransformation drawLineTransformation
  private OkHttpClient okHttpClient;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_picasso);
    ArrayList<Dish> dishList = new ArrayList<Dish>();
    drawLineTransformation = new DrawLineTransformation();
    //picasso完美衔接OKHttpClient
    okHttpClient= new OkHttpClient();
    okHttpClient.networkInterceptors().add(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
      Response originalResponse = chain.proceed(chain.request());
        return originalResponse.newBuilder().header("Cache-Control", "max-age=" + (60 * 60 * 24 * 365)).build();
      }
    });
    okHttpClient.setCache(new Cache(this.getCacheDir(), Integer.MAX_VALUE));
    OkHttpDownloader okHttpDownloader = new OkHttpDownloader(okHttpClient);
    mPicasso = new Picasso.Builder(this).downloader(okHttpDownloader).build();
    dishList.add(new Dish(BASE_URL + "42.jpg", "水煮鱼片", "38.00"));
    dishList.add(new Dish(BASE_URL + "34.jpg", "小炒肉", "18.00"));
    dishList.add(new Dish(BASE_URL + "37.jpg", "清炒时蔬", "15.00"));
    dishList.add(new Dish(BASE_URL + "12.jpg", "金牌烤鸭", "36.00"));
    dishList.add(new Dish(BASE_URL + "13.jpg", "小鸡蹲蘑菇", "40.00"));
    dishList.add(new Dish(BASE_URL + "14.jpg", "麻婆豆腐", "25.00"));
    dishList.add(new Dish(BASE_URL + "15.jpg", "北京烧鸭", "30.00"));
    dishList.add(new Dish(BASE_URL + "16.jpg", "麻辣风爪", "25.00"));
    dishList.add(new Dish(BASE_URL + "17.jpg", "宫爆鸡丁", "36.00"));
    dishList.add(new Dish(BASE_URL + "19.jpg", "椒盐虾", "45.00"));
    dishList.add(new Dish(BASE_URL + "20.jpg", "清蒸鲈鱼", "20.00"));
    ListView mListView = (ListView) this.findViewById(R.id.listview);
    MainListViewAdapter adapter = new MainListViewAdapter(dishList);
    mListView.setAdapter(adapter);
   }

   private class MainListViewAdapter extends BaseAdapter {
    private ArrayList<Dish> dishList;
    public MainListViewAdapter(ArrayList<Dish> list) {
      this.dishList = list;
    }
    @Override
    public int getCount() {
      return dishList.size();
    }
    @Override
    public Object getItem(int position) {
      return dishList.get(position);
    }
     @Override
    public long getItemId(int position) {
      return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      ListViewItemHolder item = null;
      if (convertView == null) {
        convertView = LayoutInflater.from(PicassoToOkHttpActivity.this).
inflate(R.layout.main_listview_item, null);
        item = new ListViewItemHolder();
        item.img_iv = (ImageView) convertView.findViewById(R.id.imageView1);
        item.name_textview = (TextView) convertView.findViewById(R.id.textView1);
        item.price_textview = (TextView) convertView.findViewById(R.id.textView2);
         convertView.setTag(item);
      } else {
        item = (ListViewItemHolder) convertView.getTag();
      }
      Dish dish = dishList.get(position);
      mPicasso.load(dish.getImgUrl()).transform(drawLineTransformation).into(item.img_iv);
      item.name_textview.setText(dish.getName());
      item.price_textview.setText(dish.getPrice() + "元");
      return convertView;
    }
}
对每个请求Request进行预处理
比如,有两台图片服务器,a.meizu.me/b.meizu.me,本来所有的访问地址都是a.meizu.me的,但是要在切换到b.meizu.me,那么我们就可以直接通过该接口进行处理。当然Reqeust还包含了很多其他的信息,我们后面会慢慢看到。只要是Request的相关信息,我们都可以通过这个接口进行修改。
Picasso提供接口类RequestTransformer,其代码如下:
  public interface RequestTransformer {
    /**
     * Transform a request before it is submitted to be processed.
     *
     * @return The original request or a new request to replace it. Must not be null.
     */
    Request transformRequest(Request request);

    /** A {@link RequestTransformer} which returns the original request. */
    RequestTransformer IDENTITY = new RequestTransformer() {
      @Override public Request transformRequest(Request request) {
        return request;
      }
    };
  }
}

实例
1.自定义RequestTransformer类,其代码如下:

class RequestTransformerDNS implements Picasso.RequestTransformer {
   @Override
   public Request transformRequest(com.squareup.picasso.Request request) {
      //private static final String REPLACE_URL = "https://www.baidu.com/img/bd_logo1.png";
      return  new Request.Builder(Uri.parse(REPLACE_URL)).transform(drawLineTransformation).build();
   }  

2.如何使用自定义RequestTransformer,其代码如下:

public class MainActivity extends Activity {

  private static final String BASE_URL = "http://img1.3lian.com/img2011/w1/106/85/";
  private static final String REPLACE_URL = "https://www.baidu.com/img/bd_logo1.png";
  private Picasso mPicasso;
  private Picasso.Builder mBuilder;
  private DrawLineTransformation drawLineTransformation;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_picasso);
    ArrayList<Dish> dishList = new ArrayList<Dish>();
    drawLineTransformation = new DrawLineTransformation();
    //不使用Picasso默认参数 自定义Picasso的一些参数
    mBuilder = new Picasso.Builder(MainActivity.this);
    mBuilder.requestTransformer(new RequestTransformerDNS());
    mPicasso = mBuilder.build();
    //将自己创建的mPicasso设置为单例模式
    Picasso.setSingletonInstance(mPicasso);
    dishList.add(new Dish(BASE_URL + "42.jpg", "水煮鱼片", "38.00"));
    dishList.add(new Dish(BASE_URL + "34.jpg", "小炒肉", "18.00"));
    dishList.add(new Dish(BASE_URL + "37.jpg", "清炒时蔬", "15.00"));
    dishList.add(new Dish(BASE_URL + "12.jpg", "金牌烤鸭", "36.00"));
    dishList.add(new Dish(BASE_URL + "13.jpg", "小鸡蹲蘑菇", "40.00"));
    dishList.add(new Dish(BASE_URL + "14.jpg", "麻婆豆腐", "25.00"));
    dishList.add(new Dish(BASE_URL + "15.jpg", "北京烧鸭", "30.00"));
    dishList.add(new Dish(BASE_URL + "16.jpg", "麻辣风爪", "25.00"));
    dishList.add(new Dish(BASE_URL + "17.jpg", "宫爆鸡丁", "36.00"));
    dishList.add(new Dish(BASE_URL + "19.jpg", "椒盐虾", "45.00"));
    dishList.add(new Dish(BASE_URL + "20.jpg", "清蒸鲈鱼", "20.00"));
    ListView mListView = (ListView) this.findViewById(R.id.listview);
    MainListViewAdapter adapter = new MainListViewAdapter(dishList);
    mListView.setAdapter(adapter);
   }
   private class MainListViewAdapter extends BaseAdapter {

    private ArrayList<Dish> dishList;

    public MainListViewAdapter(ArrayList<Dish> list) {
      this.dishList = list;
    }

    @Override
    public int getCount() {
      return dishList.size();
    }

    @Override
    public Object getItem(int position) {
      return dishList.get(position);
    }
    @Override
    public long getItemId(int position) {
      return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      ListViewItemHolder item = null;
      if (convertView == null) {
        convertView = LayoutInflater.from(MainActivity.this).inflate(
                        R.layout.main_listview_item, null);
        item = new ListViewItemHolder();
        item.img_iv = (ProfileView) convertView.findViewById(R.id.imageView1);
        item.name_textview = (TextView) convertView.findViewById(R.id.textView1);
        item.price_textview = (TextView) convertView.findViewById(R.id.textView2);
         convertView.setTag(item);
      } else {
        item = (ListViewItemHolder) convertView.getTag();
      }
      Dish dish = dishList.get(position);
      mPicasso.load(dish.getImgUrl()).transform(drawLineTransformation).into(item.img_iv);
      item.name_textview.setText(dish.getName());
      item.price_textview.setText(dish.getPrice() + "元");
      return convertView;
    }
自定义Dowloader
假如我们不想使用picasso内置的下载器时,也不想集成okHttp时,这时候我们就可以自己定义一套自己的下载器了
Picasso提供接口类Downloader,我们只需要实现它,或者继承它的子类既可.

实例

public class PicassoBitmapDownloader extends UrlConnectionDownloader {

    private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB
    private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
    @NonNull
    private Context context;
    @Nullable
    private DiskLruCache diskCache;

    public class IfModifiedResponse extends Downloader.Response {

        private final String ifModifiedSinceDate;

        public IfModifiedResponse(InputStream stream, boolean loadedFromCache, long contentLength, String ifModifiedSinceDate) {
            super(stream, loadedFromCache, contentLength);
            this.ifModifiedSinceDate = ifModifiedSinceDate;
        }

        public String getIfModifiedSinceDate() {

            return ifModifiedSinceDate;
        }
    }

    public PicassoBitmapDownloader(@NonNull Context context) {
        super(context);
        this.context = context.getApplicationContext();
    }

    @Override
    public Downloader.Response load(final Uri uri, int networkPolicy) throws IOException {
        final String key = getKey(uri);
        {
            Response cachedResponse = getCachedBitmap(key);
            if (cachedResponse != null) {
                return cachedResponse;
            }
        }
        IfModifiedResponse response = _load(uri);
        byte [] data = toByteArray(response.getInputStream());
        if (cacheBitmap(key, data, response.getIfModifiedSinceDate())) {
            IfModifiedResponse cachedResponse = getCachedBitmap(key);
            if (cachedResponse != null) {
                return cachedResponse;
            }
        }
        return response;
    }

    private static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n;
        while (-1 != (n = input.read(buffer))) {
            byteArrayOutputStream.write(buffer, 0, n);
        }
        return byteArrayOutputStream.toByteArray();
    }

    @NonNull
    protected IfModifiedResponse _load(Uri uri) throws IOException {

        HttpURLConnection connection = openConnection(uri);
        int responseCode = connection.getResponseCode();
        if (responseCode >= 300) {
            connection.disconnect();
            throw new ResponseException(responseCode + " " + connection.getResponseMessage(),
                    0, responseCode);
        }
        long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
        String lastModified = connection.getHeaderField("Last-Modified");
        return new IfModifiedResponse(connection.getInputStream(), false, contentLength, lastModified);
    }

    @Override
    protected HttpURLConnection openConnection(Uri path) throws IOException {

        HttpURLConnection conn = super.openConnection(path);
        DiskLruCache diskCache = getDiskCache();
        DiskLruCache.Snapshot snapshot = diskCache == null ? null : diskCache.get(getKey(path));
        if (snapshot != null) {
            String ifModifiedSince = snapshot.getString(1);
            if (!TextUtils.isEmpty(ifModifiedSince)) {
                conn.addRequestProperty("If-Modified-Since", ifModifiedSince);
            }
        }
        return conn;
    }

    @Override
    public void shutdown() {
        try {
            if (diskCache != null) {
                diskCache.flush();
                diskCache.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        super.shutdown();
    }

    @Nullable
    public IfModifiedResponse getCachedBitmap(String key) {
        try {
            DiskLruCache diskCache = getDiskCache();
            DiskLruCache.Snapshot snapshot = diskCache == null ? null : diskCache.get(key);
            if (null != snapshot) {
                String content = snapshot.getString(1);
                InputStream inputStream = snapshot == null ? null : snapshot.getInputStream(0);
                long fileLength = snapshot.getLength(0);
                if (inputStream == null) {
                    return null;
                }
                return new IfModifiedResponse(inputStream, true, fileLength, content);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取DiskLruCache
     * @return
     */
    @Nullable
    synchronized public DiskLruCache getDiskCache() {

        if (diskCache == null) {
            try {
                File file = new File(context.getCacheDir() + "/images");
                if (!file.exists()) {
                    file.mkdirs();
                }
                long maxSize = calculateDiskCacheSize(file);
                diskCache = DiskLruCache.open(file, BuildConfig.VERSION_CODE, 2, maxSize);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return diskCache;
    }

    /**
     * 缓存bitmap到磁盘上
     * @param key
     * @param data
     * @param ifModifiedSince
     * @return
     */
    public boolean cacheBitmap(@Nullable String key, @Nullable byte[] data, @Nullable String ifModifiedSince) {
        OutputStream os = null;
        DiskLruCache.Editor editor = null;
        try {
            DiskLruCache diskCache = getDiskCache();
            if (diskCache == null) {
                return false;
            }
            editor = diskCache.edit(key);
            if (editor == null) {
                return false;
            }
            os = editor.newOutputStream(0);
            os.write(data);
            editor.set(1, ifModifiedSince == null ? "" : ifModifiedSince);
            editor.commit();
        } catch (Throwable x) {
            if (null != editor) {
                try {
                    editor.abort();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            Log.d("x in writeToDiskCache", x.getMessage());
        } finally {
            ChatUtils.close(os);
        }
        return true;
    }

    /**
     * 通过uri获取对应的key
     * @param uri
     * @return
     */
    public static String getKey(Uri uri) {
        String cacheKey;
        String uriKey = uri.toString();
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(uriKey.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(uriKey.hashCode());
        }
        return cacheKey;
    }

    private static String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    /**
     * 计算当前目录剩余空间
     * @param dir
     * @return
     */
    static long calculateDiskCacheSize(File dir) {

        long available = ChatUtils.getUsableSpace(dir);
        // Target 2% of the total space.
        long size = available / 50;
        // Bound inside min/max size for disk cache.
        return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
    }
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值