1.简介
picasso是Square公司开源的一个Android图形缓存库。可以实现图片下载和缓存功能。
主要有以下一些特性:
1.在adapter中回收和取消当前的下载。
2.使用最少的内存完成复杂的图形转换操作。
3.自动的内存和硬盘缓存。
4.图形转换操作,如变换大小,旋转等,提供了接口来让用户可以自定义转换操作。
5.加载载网络或本地资源。
2.Picasso框架设计图
如上图,整个库分为 Dispatcher,RequestHandler 及 Downloader,PicassoDrawable 等模块。
Dispatcher 负责分发和处理 Action,包括提交、暂停、继续、取消、网络状态变化、重试等等。
简单的讲就是 Picasso 收到加载及显示图片的任务,创建 Request 并将它交给 Dispatcher,Dispatcher 分发任务到具体 RequestHandler,任务通过 MemoryCache 及 Handler(数据获取接口) 获取图片,图片获取成功后通过 PicassoDrawable 显示到 Target 中。
需要注意的是上面 Data 的 File system 部分,Picasso 没有自定义本地缓存的接口,默认使用 http 的本地缓存,API 9 以上使用 okhttp,以下使用 Urlconnection,所以如果需要自定义本地缓存就需要重定义 Downloader。
3.Picasso框架特点
1.自带统计监控功能。支持图片缓存使用的监控,包括缓存命中率、已使用内存大小、节省的流量等。
2.支持优先级处理。每次任务调度前会选择优先级高的任务,比如 App 页面中 Banner 的优先级高于Icon时就很适用。
3.支持延迟到图片尺寸计算完成加载。
4.支持飞行模式、并发线程数根据网络类型而变。手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数,比如 wifi 最大并发为 4, 4g 为 3,3g 为 2。这里 Picasso 根据网络类型来决定最大并发数,而不是 CPU 核数。
5.“无”本地缓存。无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及Expired 控制图片的过期时间。
4.代码说明
4.1.AndroidStudio配置
implementation 'com.squareup.picasso:picasso:2.71828'
4.2.基本使用
imageview=findViewById(R.id.activity_picass_imageview);
String url="XXX";
Picasso.get()
.load(url)
.into(imageview);
.get():获取Picasso对象。
.load(url):下载url对应的图片。
.into(imageview):显示ImageView。
4.3.加载其他路径下的图片
4.3.1.加载本地图片 比如sd卡中的图片
imageview=findViewById(R.id.activity_picass_imageview);
File file = new File(Environment.getExternalStorageDirectory() + "/icon.png");
Picasso.get()
.load(file)
.into(imageview);
4.3.2.加载应用资源 比如mipmap下的图片
imageview=findViewById(R.id.activity_picass_imageview);
int resource = R.mipmap.za;
Picasso.get()
.load(resource)
.into(imageview);
4.3.3.加载应用资源 比如asset下的图片
imageview=findViewById(R.id.activity_picass_imageview);
String prefixurl="file:///android_asset/";
Picasso.get()
.load(prefixurl+"tab_my_pressed.png")
.into(imageview);
4.3.4.加载Uri对象 比如获取的相机相册图片
imageview=findViewById(R.id.activity_picass_imageview);
Uri imageUri =XXX;
Glide.with(this)
.load(imageUri)
.into(imageview);
附:load重载方法
4.4.添加占位符
imageview=findViewById(R.id.activity_picass_imageview);
String url="XXX";
Picasso.get()
.load(url)
.placeholder(R.drawable.ic_launcher_background)
.error(R.drawable.ic_launcher_background)
.into(imageview);
.placeholder(R.drawable.ic_launcher_background):图片加载中显示设置图片。
.error(R.drawable.ic_launcher_background):图片下载失败显示设置图片。
4.5.裁剪图片尺寸
imageview=findViewById(R.id.activity_picass_imageview);
String url="XXX";
Picasso.get()
.load(url)
.resize(20,20)
.into(imageview);
.resize(20,20):将图片剪切成20*20大小的图片(单位px)。
同时,resize方法经常和centerCrop(),centerInside()等方法一起使用。但是后两者不可单独使用。
附1:官方定义剪切
英:Transform images to better fit into layouts and to reduce memory size。
中:转换图像以更好地适应布局和减少内存大小。
附2:计算出最佳的大小及最佳的图片质量来进行图片展示 ( 减少内存 )
imageview=findViewById(R.id.activity_picass_imageview);
String url="http://i.imgur.com/DvpvklR.png";
Picasso.get()
.load(url)
.fit()
.into(imageview);
即.fit()方法。
4.6.加载圆形图片
4.6.1.自定义Transformation实现类
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;
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
if (squaredBitmap != source) {
source.recycle();
}
Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
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";
}
}
注:也可自定义半径
float r = radius;
private Context mContext;
private int radius; // 圆形图片半径
public CircleTransform(Context context) {
mContext = context;
radius = ActivityUtils.dip2px(mContext, 10);
}
4.6.2.使用
imageview=findViewById(R.id.activity_picass_imageview);
String url="XXX";
Picasso.get()
.load(url)
.transform(new CircleTransform())
.into(imageview);
使用.transform(new CircleTransform())方法,将自定义的Transformation实现类导入即可。
4.7.加载圆角图片
4.7.1.自定义Transformation实现类
public class RoundTransform implements Transformation {
private Context mContext;
public RoundTransform(Context context) {
mContext = context;
}
@Override
public Bitmap transform(Bitmap source) {
int widthLight = source.getWidth();
int heightLight = source.getHeight();
int radius = ActivityUtils.dip2px(mContext, 10); // 圆角半径
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
Paint paintColor = new Paint();
paintColor.setFlags(Paint.ANTI_ALIAS_FLAG);
RectF rectF = new RectF(new Rect(0, 0, widthLight, heightLight));
canvas.drawRoundRect(rectF, radius, radius, paintColor);
Paint paintImage = new Paint();
paintImage.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
canvas.drawBitmap(source, 0, 0, paintImage);
source.recycle();
return output;
}
@Override
public String key() {
return "roundcorner";
}
}
4.7.2.使用
imageview=findViewById(R.id.activity_picass_imageview);
String url="XXX";
Picasso.get()
.load(url)
.transform(new RoundTransform(PicassoActivity.this))
.into(imageview);
使用.transform(new RoundTransform(PicassoActivity.this))方法,将自定义的Transformation实现类导入即可。
附:transform方法
注意:设置圆角图片时,如果ImageView设置如下类型
android:scaleType="centerCrop"
则圆角没有效果。
4.8.加载图片优先级
因为Picasso是异步加载,所以多个图片时那个图片先加载出来是不一定的。如果用户想自定义图片加载的先后顺序。Picasso支持设置优先级,分为HIGH, NORMAL, 和 LOW,所有的加载默认优先级为NORMAL。
代码:
imageview=findViewById(R.id.activity_picass_imageview);
String url="http://i.imgur.com/DvpvklR.png";
Picasso.get()
.load(url)
.priority(Picasso.Priority.HIGH)
.into(imageview);
priority(Picasso.Priority.HIGH):高
priority(Picasso.Priority.NORMAL):正常(默认)
priority(Picasso.Priority.LOW):低
4.9.旋转图片
imageview=findViewById(R.id.activity_picass_imageview);
String url="http://i.imgur.com/DvpvklR.png";
Picasso.get()
.load(url)
.rotate(90)
.into(imageview);
.rotate(90):顺时针旋转90°。(默认圆心(0,0))。
.rotate(float degrees, float pivotX, float pivotY):自定义圆心。
4.10.Tag设置
imageview=findViewById(R.id.activity_picass_imageview);
String url="http://i.imgur.com/DvpvklR.png";
Picasso.get()
.load(url)
.tag(1)
.into(imageview);
.tag(1)方法:
4.11.内存问题
无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。
imageview=findViewById(R.id.activity_picass_imageview);
String url="http://i.imgur.com/DvpvklR.png";
Picasso.get()
.load(url)
.memoryPolicy(NO_CACHE, NO_STORE)
.into(imageview);
4.12.列表使用
Picasso实现了图片的异步加载,并解决了Android中加载图片时常见的一些问题,它有以下特点:
在Adapter中取消了不在视图范围内的ImageView的资源加载,因为可能会产生图片错位。
使用复杂的图片转换技术降低内存的使用。
自带内存和硬盘的二级缓存机制。
代码:
Adapter:
public class PicassoAdapter extends BaseAdapter {
private Context context;
private List list;
public PicassoAdapter(Context context,List list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
if (convertView == null) {
convertView = View.inflate(context, R.layout.glidelistview_item, null);
viewHolder = new ViewHolder();
viewHolder.circleImageView = convertView.findViewById(R.id.glidelistview_circleimageview);
viewHolder.textView1 = convertView.findViewById(R.id.glidelistview_item_textview1);
viewHolder.textView2 = convertView.findViewById(R.id.glidelistview_item_textview2);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.textView1.setText(list.get(position).getName() + "【" + (position + 1) + "】");
viewHolder.textView2.setText(list.get(position).getAge());
String ava = list.get(position).getAva();
final int picassoTag=position;
if(!BooleanUtils.isEmpty(ava)){//头像不为空
//文字显示颜色
viewHolder.textView1.setTextColor(context.getResources().getColor(R.color.colorPrimary));
viewHolder.textView2.setTextColor(context.getResources().getColor(R.color.colorPrimary));
//Picasso加载图片
Picasso.get()
.load(ava)
.placeholder(R.mipmap.patient_ava)
.error(R.mipmap.patient_ava)
.tag(picassoTag)
.transform(new CircleTransform())
.into(viewHolder.circleImageView);
}else{//头像为空
//文字显示颜色
viewHolder.textView1.setTextColor(context.getResources().getColor(R.color.colorAccent));
viewHolder.textView2.setTextColor(context.getResources().getColor(R.color.colorAccent));
//默认图片
viewHolder.circleImageView.setBackgroundResource(R.mipmap.patient_ava);
}
return convertView;
}
public static class ViewHolder {
private ImageView circleImageView;
private TextView textView1;
private TextView textView2;
}
}
Activity:
public class PicassoListActivity extends AppCompatActivity {
private ListView listview;
private PicassoAdapter adapter;
private List list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_glide);
onWindowbar();
initData();
}
/**
* 赋值
*/
private void initData() {
listview=findViewById(R.id.activity_listview_listview);
list=new ArrayList<>();
Student student1=new Student();
student1.setName("詹姆斯");
student1.setAva("XXX");
student1.setAge(student1.getName()+":很懒什么也没留下!");
Student student2=new Student();
student2.setName("韦德");
student2.setAva("XXX");
student2.setAge(student2.getName()+":很懒什么也没留下!");
Student student3=new Student();
student3.setName("山东鲁能");
student3.setAva("XXX");
student3.setAge(student3.getName()+":很懒什么也没留下!");
Student student4=new Student();
student4.setName("保罗");
student4.setAva("XXX");
student4.setAge(student4.getName()+":很懒什么也没留下!");
Student student5=new Student();
student5.setName("安东尼");
student5.setAva("XXX");
student5.setAge(student5.getName()+":很懒什么也没留下!");
Student student6=new Student();
student6.setName("蒿俊闵");
student6.setAva("XXX");
student6.setAge(student6.getName()+":很懒什么也没留下!");
Student student7=new Student();
student7.setName("李霄鹏");
student7.setAva("");
student7.setAge(student7.getName()+":没有上传头像!");
for(int i=0;i<30;i++){
list.add(student1);
list.add(student2);
list.add(student3);
list.add(student4);
list.add(student5);
list.add(student6);
list.add(student7);
}
adapter=new PicassoAdapter(PicassoListActivity.this,list);
listview.setAdapter(adapter);
}
/**
* onKeyDown方法
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
finish();
overridePendingTransition(0, R.anim.activity_right_open);
return false;
}
return super.onKeyDown(keyCode, event);
}
}
附1:框架GitHub链接
https://github.com/square/picasso
附2:
一个GitHub上的各种形状的ImageView链接:https://github.com/vinc3m1/RoundedImageView
附3:几种图片框架对比图