带你全方位学习Picasso(一)
引言
picasso是非常有名的一个图片加载缓存框架。每个强大的框架往往使用是非常简单的,但是背后还有很多不容忽视的功能或者技巧,Picasso也是如此。
目录
- Picasso入门
- Picasso高级
- 在适配器(ListView,GridView…)中使用
- 列表中处理null/空值
- placeholder(),error(),淡入淡出
- 图像缩放,大小调整
Picasso入门
添加到依赖
首选,你需要添加依赖,当前我们会使用最新的版本 2.5.2
,添加依赖有众所周知的两种方式。
Gradle
compile 'com.squareup.picasso:picasso:2.5.2'
Maven
<dependency>
<groupId>com.squareup.picasso</groupId>
<artifactId>picasso</artifactId>
<version>2.5.2</version>
</dependency>
基本的加载图片
Picasso使用的链式编程,它至少需要三个参数才能完成基本的图像请求
with(Context context)
- 很多Androd API
会去调用,这里也没区别。load(String imageUrl)
- 这里指应该加载哪个图像,大多数情况下,我们会使用String ,代表一个url图像。into(ImageView targetImageView)
- 目标显示的ImageView
不妨来个简单的例子。
ImageView targetImageView = (ImageView) findViewById(R.id.imageView);
String internetUrl = "http://i.imgur.com/DvpvklR.png";
Picasso
.with(context)
.load(internetUrl)
.into(targetImageView);
到此,只要保证url众的Image是没问题的,稍等片刻就能见到你想要的图片,如果图像不存在,Picasso将返回到错误回调,如何请求错误回调,稍后我会做说明。简单的三句话就已经让你完成了图片请求,但是这只是冰上的一角。
Picasso高级
除了简单的使用,我们会写发现load是方法是一个重载的方法。其实除了基本的String类型加载,我们还可以尝试从各个渠道去加载我们想要的图片
资源加载
int resourceId = R.mipmap.ic_launcher;
Picasso
.with(context)
.load(resourceId)
.into(imageViewResource);
文件加载
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Running.jpg");
Picasso
.with(context)
.load(file)
.into(imageViewFile);
Uri加载
Uri uri = resourceIdToUri(context, R.mipmap.future_studio_launcher);
Picasso
.with(context)
.load(uri)
.into(imageViewUri);
public static final String ANDROID_RESOURCE = "android.resource://";
public static final String FOREWARD_SLASH = "/";
private static Uri resourceIdToUri(Context context, int resourceId) {
return Uri.parse(ANDROID_RESOURCE + context.getPackageName() + FOREWARD_SLASH + resourceId);
}
在适配器(ListView,GridView…)中使用
首先,我们需要一些测试图像。
public static String[] eatFoodyImages = {
“http://i.imgur.com/rFLNqWI.jpg“,
“http://i.imgur.com/C9pBVt7.jpg“,
“http://i.imgur.com/rT5vXE1.jpg“,
“http://i.imgur.com/aIy5R2k.jpg“,
“http://i.imgur.com/MoJs9pT.jpg“,
“http://i.imgur.com/S963yEM.jpg“,
“http://i.imgur.com/rLR2cyc.jpg“,
“http://i.imgur.com/SEPdUIx.jpg“,
“http://i.imgur.com/aC9OjaM.jpg“,
“http://i.imgur.com/76Jfv9b.jpg“,
“http://i.imgur.com/fUX7EIB.jpg“,
“http://i.imgur.com/syELajx.jpg“,
“http://i.imgur.com/COzBnru.jpg“,
“http://i.imgur.com/Z3QjilA.jpg“,
};我们需要一个Activity,在这之中创建一个Adapter,并为ListView适配。
public class UsageExampleAdapter extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);setContentView(R.layout.activity_usage_example_adapter); listView.setAdapter(new ImageListAdapter(UsageExampleAdapter.this, eatFoodyImages)); }
}
3.接下来,怎么可以少了Adapter的布局文件,非常的简单,就是我们想要显示的 ImageView
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"/>
4.最后,我们就需要去实现 ImageListAdapter
。
public class ImageListAdapter extends ArrayAdapter {
private Context context;
private LayoutInflater inflater;
private String[] imageUrls;
public ImageListAdapter(Context context, String[] imageUrls) {
super(context, R.layout.listview_item_image, imageUrls);
this.context = context;
this.imageUrls = imageUrls;
inflater = LayoutInflater.from(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = inflater.inflate(R.layout.listview_item_image, parent, false);
}
Picasso
.with(context)
.load(imageUrls[position])
.fit() // 稍后解释
.into((ImageView) convertView);
return convertView;
}
}
重要的地方是在
getView()
这个方法中。当然你会发现Picasso的使用没有太多的变化。专门拿这篇文章去讲解这个看起来不值得,其实背后的Picasso它会在滚动的时自动取消图片请求,清除图片和加载正确的图像到ImageView
。
注 后面我们会进一步说明优化适配器的使用 fit()与tags()
。
Picasso的力量:cache
当你在反复的上下滚动,你会看到图像显示比以前快多了。我想你一定是猜到了,这些图片来自高速缓存,无需再从网络加载。Picasso针对加载图片主要源自三个源: 内存-> 磁盘 -> 网络
。最后,有什么是你需要做的,Picasso都已经为了做好了!甚至可以更改缓存的大小,这些我们后续会讲解。
列表中处理null/空值
在使用Picasso中,或许我们会遇到两个问题: IllegalArgumentException: Path must not be empty
,再者就是不完整图片映射,为了确保程序能够正常运行,我们会给出两种方式解决问题。
取消Picasso的图片请求
如果不打算加载,你需要取消这个请求,使用 cancelRequest()
。可以确保这个ImageView没有请求回调。如果用户滚动飞快,可能会发生 ListView与imageView重用。取消请求可以避免不正确的图像显示。
使用占位符
例外一种就是使用占位符,说简单点就是使用默认图片
代码示例
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = inflater.inflate(R.layout.listview_item_image, parent, false);
}
ImageView imageView = (ImageView) convertView;
if (TextUtils.isEmpty(imageUrls[position])) {
// option 1: 取消Picasso的图片请求
Picasso
.with(context)
.cancelRequest(imageView);
imageView.setImageDrawable(null);
// option 2: 使用占位符
/*
Picasso
.with(context)
.load(R.drawable.floorplan)
.into(imageView);
*/
}
else {
Picasso
.with(context)
.load(imageUrls[position])
.fit() // will explain later
.into(imageView);
}
return convertView;
}
placeholder(),error(),淡入淡出
.placeholder() 占位符
我们差点忘记去考虑一个问题:空的ImageView体验非常的不友好,如果你使用Picasso网络加载图片,不同的网络环境会导致不能立即加载出图像,这时候就用到了我们的占位符 .placeholder()
。 他会预先加载一张图片(当然,是本地的),直到网络图片加载成功为止。
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher)
.into(imageViewPlaceholder);
.error() 错误占位符
假设我们加载的图片链接已经失效了,我们可以通过Picasso的错误回调选取合适的操作,但是这样未免太复杂了,大多数的情况只需要调用 error()
即可。
Picasso
.with(context)
.load("http://futurestud.io/non_existing_image.png")
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.future_studio_launcher)
.into(imageViewError);
使用 noFade()
如果你想直接显示图片不使用淡入淡出的效果,可以使用 noFade()。
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.future_studio_launcher)
.noFade()
.into(imageViewFade);
使用noPlaceholder()
我们来想一下下面这种情况:你在ImageView中加载了一张图片,一段时间过后,你想在同样的ImageView中加载另一张图片。使用默认的设置,你在重新调用Picasso的时候,ImageView将会加载之前设置的placeholder的图片。如果这个ImageView在你的UI中很重要的话,在几秒钟内快速的改变ImageView的图片会看起来很不合适。一个比较好的解决方案是调用 .noPlaceholder()
这个方法。在第二张图片加载之前,ImageView会一直显示第一张图片。这对用户来说会很友好。
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher)
.into(imageViewnoPlaceholder, new Callback() {
@Override
public void onSuccess() {
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[1])
.noPlaceholder()
.into(imageViewNoPlaceholder);
}
@Override
public void onError() {
}
});
图像缩放,大小调整
通过resize(x,y)改变大小
通常来说,如果你的服务器的图片刚好吻合你图像的尺寸,这将会很好的平衡带宽,内存消耗,图像质量,但是这些总是无法如愿以偿。如果得到的图像是一个无法满足现象的大小,可以使用 resize(horizontalSize, verticalSize)
来改变图片的大小。
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.resize(600, 200)
.into(imageViewResize);
使用scaleDown()
如果我们调用了 resize(x,y)
方法的话,Picasso一般会重新计算以改变图片的加载质量,比如一张小图变成一张大图进行展示的时候,但是如果我们的原图是比我们从新resize的新图规格大的时候,我们就可以调用onlyScaleDown()来直接进行展示而不再重新计算.
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.resize(6000, 2000)
.onlyScaleDown() //如果它大于6000x2000像素,将仅对图像进行调整大小。
.into(imageViewResizeScaleDown);
避免图像缩放与拉伸
当我们操作图片时,原图与目标的尺寸比例,会导致失真。为了防止这种情况发生。Picasso提供了两种解决的方法:调用 centerCrop()或centerInside()
。
centerCrop()
centerCrop()使用了一种裁剪技术去缩放图片,去填充ImageView的界限,然后裁剪掉多余的部分。使用这个方法会使ImageView会被填充的很合适,但是图片可能不能完全显示出来。
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.resize(600, 200) // resizes the image to these dimensions (in pixel)
.centerCrop()
.into(imageViewResizeCenterCrop);
CenterInside()
centerInside()也是一种裁剪技术,缩放时图片的宽和高同时等于或小于设定的ImageView的边界。图片会被完全显示出来,但是也许不能完全填充ImageView。
Picasso
.with(this)
.load(eatFoodyImages[0])
.resize(600, 200)
.centerInside()
.into(imageView);
fit()
前面介绍的功能以及可以基本解决你的缩放需求了,这里还有一个功能会非常有用:fit()。
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.fit()
// call .centerInside() or .centerCrop() to avoid a stretched image
.into(imageViewFit);
fit()会去测量ImageView的宽高,并且在内部使用resize(),从而缩小图片的尺寸去适配ImageView。关于fit()有两点必须知道:首先,调用fit()会延时图片请求,因为Picasso需要等待直到ImageView的尺寸被测量完毕;其次,只可以使用一个ImageView做完fit的目标(稍后我们会看看其他目标)。
优势是图片使用的是最低的分辨率,不会受到图片质量的影响。更低的分辨率意味着需要占用更少的缓存。
总结
到此,Picasso的第一阶段学习就先到这里,在之前博主对于Picasso的使用大部分停留在这一部分,是的,这里足够使用了。如果你还有充足的时间不妨接着往下看,因为它还有更多小功能。