昨天带领大家熟悉了一下Picasso框架加载图片的一个基本流程的源码,俗话说,实践是检验真理的唯一标准,有了理论的知识,不知道如何进行使用,那么你所知道的理论知识就没有什么实际意义了,那么今天就带领大家玩转一下Picasso的实际应用。
通过本篇文章的学习,我相信你会学习到以下几个方面:
1.Picasso如何进行加载图片
2.Picasso如何监听加载图片的结果
3.Picasso将加载完成的图片做成圆形
4.Picasso加载listview的item图片时,如何实现用户滑动时,Picasso暂停当前加载图片,用户滑动结束时,Picasso继续加载图片。
5.Picasso缓存策略控制
1.加载网络图片:
Picasso.with(context)
//加载图片地址
.load(Uri uri)
//设置图片配置信息
.config(Bitmap.Config.RGB_565)
//设置占位图
.placeholder(R.mipmap.ic_launcher)
//设置加载图片失败时的占位图
.error(R.mipmap.ic_launcher)
//按照控件的大小进行图片放大缩小
.fit()
//目标控件
.into(holder.iv_image);
通过上面的方法就可以很简单的实现一张图片的加载。至于Picasso是如何做到的,这里不再做过多说明,上一篇文章已经讲解的很清楚。
针对这个方法,我这里特别强调一点的是当你给一个控件设置了一个fit()方法后,后面就不可以调用resize()方法,这点大家要特别记住一下,至于为什么,我想应当是这2个方法都是针对图片进行裁剪的,如果你调用了其中的任何一个方法,那么很显然原图bitmap已经被破坏掉了。
2.加载SD卡上的图片
File file=new File(path);
Picasso.with(context).load(file).config(Bitmap.Config.RGB_565).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).fit().into(holder.iv_image)
很简单,只是load()方法里面传入一个File对象即可。
3.加载assets目录下面的文件(假如文件夹里面有一张ss.png图片)
//1.获取输入流
InputStream is=getAssets().open("ss.png");
//2.将流转成File对象
File fil2=newFile(Environment.getExternalStorageDirectory().getPath()+File.separator+"xxx.png");
if(!fil2.exists()){
fil2.createNewFile();
}
//通过Picasso进行加载和加载sd卡上的图片一样
Picasso.with(context).load(file).config(Bitmap.Config.RGB_565).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).fit().into(holder.iv_image)
public void inputstreamtofile(InputStream ins,File file){
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = ins.read(buffer) )!= -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
}catch(Exception e){
e.printStackTrace();
}
}
加载assets目录下的文件,只需要将文件转成输入流,然后转成File,最后通过Picasso进行加载显示图片。
要想监听Picasso加载图片的结果,其实很简单。只需要在into()方法里面传入一个Callback即可。
下面我们看一下Callback的源码:
public interface Callback {
void onSuccess();
void onError();
public static class EmptyCallback implements Callback {
@Override public void onSuccess() {
}
@Override public void onError() {
}
}
}
很显然,里面只有2个回调方法,一个是成功的onSuccess(),一个是失败的onError()。
Picasso.with(context).load(bean.getImgUrl()).config(Bitmap.Config.RGB_565).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).fit().into(holder.iv_image,new Callback.EmptyCallback(){
@Override
public void onSuccess() {
super.onSuccess();
Log.e("onSuccess","图片加载成功");
}
@Override
public void onError() {
super.onError();
Log.e("onError","图片加载失败");
}
});
想要实现这个功能,我们的思路应当是获取加载完后的Bitmap,然后操作这个Bitmap,把Bitmap切成一个圆形。想要获取原始的Bitmap,我们先要了解一下Transformation这个类
public interface Transformation {
//对Bitmap进行转换操作
Bitmap transform(Bitmap source);
//一个key 主要是缓存的key的生成和它有关
String key();
}
Transformation是一个接口,里面暴露了2个方法。
**
* Created by Yan on 2015/12/29.
*/
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(bean.getImgUrl()).transform(new CircleTransform())
.placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).fit().into(holder.iv_image);
效果演示:
想要实现这个功能,需要用到tag(context)这个方法
下面看一下Adapter代码:
public class MyAdapter extends BaseAdapter{
private Context context;
private List<Person> list;
private LayoutInflater mInflater;
public ViewHolder holder;
public MyAdapter(Context context, List<Person> list) {
this.context = context;
this.list = list;
this.mInflater=LayoutInflater.from(context);
}
@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) {
holder=null;
if(convertView==null){
convertView=mInflater.inflate(R.layout.itemone, null);
holder=new ViewHolder();
holder.iv_image=(ImageView) convertView.findViewById(R.id.iv_image);
holder.tv_name=(TextView) convertView.findViewById(R.id.tv_name);
convertView.setTag(holder);
}
else{
holder=(ViewHolder) convertView.getTag();
}
Person bean=list.get(position);
holder.tv_name.setText(bean.getName());
Picasso.with(context).load(bean.getImgUrl()).transform(new CircleTransform()).placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).fit().tag(context).memoryPolicy(MemoryPolicy.NO_STORE,MemoryPolicy.NO_CACHE).into(holder.iv_image);
return convertView;
}
class ViewHolder{
private TextView tv_name;
private ImageView iv_image;
}
}
/** * Created by Yan on 2015/12/29. */
public class MyScrollListener implements AbsListView.OnScrollListener {
private final Context context;
public MyScrollListener(Context context) {
this.context = context;
}
@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(context);
} else {
picasso.pauseTag(context);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
// Do nothing.
}
}
Activity调用时只需要设置OnScrollListener对象时,传入MyScrollListener即可。
listView.setOnScrollListener(new MyScrollListener(this));
通过上面的介绍,完全就可以实现了用户在滑动listview时,不会进行图片的加载,当用户离开listview时,才会继续进行加载图片。
5.Picasso缓存策略控制
说到缓存,一般就2种,一种是内存缓存,一种是文件缓存,至于这2种缓存的大小,还有文件缓存的具体位置在哪里这里不做介绍,上篇文章已经讲解。
要想了解Picasso缓存的策略,需要了解下面2个类:
//网络策略
public enum NetworkPolicy {
//不检查文件缓存(DiskCache),强制请求网络
NO_CACHE(1 << 0),
//不将请求的结果缓存到文件缓存中(DiskCache)
//注意:此属性只针对用户以okhttp框架发起的请求
NO_STORE(1 << 1),
//发送的请求只检查文件缓存,不会进行网络请求
OFFLINE(1 << 2);
public static boolean shouldReadFromDiskCache(int networkPolicy) {
return (networkPolicy & NetworkPolicy.NO_CACHE.index) == 0;
}
public static boolean shouldWriteToDiskCache(int networkPolicy) {
return (networkPolicy & NetworkPolicy.NO_STORE.index) == 0;
}
public static boolean isOfflineOnly(int networkPolicy) {
return (networkPolicy & NetworkPolicy.OFFLINE.index) != 0;
}
final int index;
private NetworkPolicy(int index) {
this.index = index;
}
}
//内存缓存策略
public enum MemoryPolicy {
//发送请求时,不会检查内存缓存
NO_CACHE(1 << 0),
//不会加请求成功后的结果加到内存缓存中
NO_STORE(1 << 1);
static boolean shouldReadFromMemoryCache(int memoryPolicy) {
return (memoryPolicy & MemoryPolicy.NO_CACHE.index) == 0;
}
static boolean shouldWriteToMemoryCache(int memoryPolicy) {
return (memoryPolicy & MemoryPolicy.NO_STORE.index) == 0;
}
final int index;
private MemoryPolicy(int index) {
this.index = index;
}
}
既然我们知道了缓存的策略,下面看我们如何实现玩转缓存策略:
1.需求:每次请求网络都是走网络,永远不使用内存缓存和文件缓存
Picasso.with(context)
.load(path)
.fit()
.networkPolicy(NetworkPolicy.NO_STORE,NetworkPolicy.NO_CACHE)
.memoryPolicy(MemoryPolicy.NO_STORE,MemoryPolicy.NO_CACHE)
.into(imageView);
2.需求:每次请求网络,只启用内存缓存,不启用二级缓存文件缓存(DiskCache)
Picasso.with(context)
.load(path)
.fit()
.networkPolicy(NetworkPolicy.NO_STORE,NetworkPolicy.NO_CACHE)
.into(imageView);
3.需求:每次请求网络,只启用二级缓存(DiskCache),不启用一级缓存(LRUCache)
Picasso.with(context)
.load(path)
.fit()
.memoryPolicy(MemoryPolicy.NO_STORE,MemoryPolicy.NO_CACHE)
.into(imageView);
当然,如果你什么都没有配置,Picasso默认是内存缓存和文件缓存都是启用的。
知道上面这些方法,大家会对Picasso的缓存有了一个基本的了解和掌握了,下面我针对一些特殊问题,提出自己的一些理解。
需求:加载一张头像图片,服务器改变头像即使路径不变,如何每次请求会保证本地的图片和服务器的图片时刻同步
解决方案:针对这类需求,采用如下方法解决。
假设你的服务器上面保存的头像路基为path。
//刷新内存缓存,从内存缓存中移除此path路径缓存的图片
Picasso.with(this).invalidate(Uri.parse(path));
注意:上面这个方法,只是将该图片从内存缓存中移除,本地缓存中不会移除。
所以我们的解决方案如下:
1.Picasso.with(this).invalidate(Uri.parse(path));
//不启用文件缓存
2. Picasso.with(context)
.load(path)
.fit()
.networkPolicy(NetworkPolicy.NO_STORE,NetworkPolicy.NO_CACHE)
.into(imageView);
通过这2种方式配置的话,既可以达到上面的需求,同时用户体验也很好,不会在界面上加载过图片后,在划回来看有加载过程中闪烁的效果。
说了这么多,大家基本可以玩转Picasso的缓存策略了,下面提供一种修改Picasso默认文件的缓存路径的方法(只对okhttp发送请求有效)
gradle引用okhttp的方式:
compile files('libs/okhttp-2.7.0.jar')
compile files('libs/okio-1.6.0.jar')
//创建文件缓存路径
String imageCacheDir =Environment.getExternalStorageDirectory()
.getPath()+File.separator+"picasso";
File file=new File(imageCacheDir);
if(!file.exists()){
file.mkdirs();
}
Picasso picasso = new Picasso.Builder(this).downloader(
new OkHttpDownloader(new File(imageCacheDir))).build();
Picasso.setSingletonInstance(picasso);
将上段代码在你的Application的oncreate方法里面实现,即可以实现修改Picasso文件缓存的默认路径。
注意:如果大家在平时测试时,尽量调用下面一行代码:
Picasso.with(this).setIndicatorsEnabled(true);
这行代码会将你加载的图片的来源通过颜色的方式在图片的左上角有展示:
红色:代表从网络下载的图片
黄色:代表从磁盘缓存加载的图片
绿色:代表从内存中加载的图片
通过上面的简单介绍,相信大家对本篇文章所提到的的问题都会有一个基本的认识,我相信只要你把这些全部都理解了,那么Picasso缓存方面的一些需求,我想大家都可以轻松解决了,下面赶紧动手操练一下吧,上述内容,都是本热学习总结而得,谢谢大家支持,今天Picasso方面的实战就讲解到这里,哇,我要去睡觉了~.