This article will learn how to use Picasso to show pictures by imitating the picture list of mobile QQ chat page, as well as some solutions to some problems in the picture list, complete example code address, complimentary a BaseRecyclerViewAdapter, you are welcome!
Take a look at the effect of mobile QQ first
The data source
Just to keep things simple let’s look up 100 local images from the album
new Thread(new Runnable() {
@Override
public void run(a) {
Cursor mCursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA,
MediaStore.Images.Media.WIDTH, MediaStore.Images.Media.HEIGHT},
MediaStore.Images.Media.MIME_TYPE + "=? OR " + MediaStore.Images.Media.MIME_TYPE + "=?".new String[] { "image/jpeg"."image/png" }, MediaStore.Images.Media._ID + " DESC");
if (mCursor == null) return;
// Take 100 images
while (mCursor.moveToNext() && mImageList.size() < MAX_IMAGE) {
long id = mCursor.getLong(mCursor.getColumnIndex(MediaStore.Images.Media._ID));
Log.i(TAG, "MediaStore.Images.Media_ID=" + id + "");
String path = mCursor.getString(mCursor.getColumnIndex(MediaStore.Images.Media.DATA));
int width = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.WIDTH));
int height = mCursor.getInt(mCursor.getColumnIndex(MediaStore.Images.Media.HEIGHT));
Image image = new Image(Uri.fromFile(new File(path)), width, height);
mImageList.add(image);
}
mCursor.close();
runOnUiThread(new Runnable() {
@Override
public void run(a) { mImageListAdapter.addAllData(mImageList); mImageListAdapter.notifyDataSetChanged(); }}); } }).start();Copy the code
The above code retrieves up to 100 images locally and stores the image information in the ImageInfo class, which is passed to adapter through a list. The main attributes of the ImageInfo class are as follows
private final Uri mUri;
private int mWidth;
private int mHeight;
private boolean mNeedResize;Copy the code
The mNeedResize property here, which tells us if we need to recalculate the width and height of the image, and we’ll talk about how to use that, and now we have our data source ready, okay
Solve picture hopping problem
This is a common problem because loading an image is an asynchronous process, and if the Bitmap does not correspond to the correct ImageView, the image will jump around. There are many ways to solve this problem, but one of them is to attach a tag to the ImageView. The Adapter code is as follows:
public void onBindViewHolder(final ImageListAdapter.ImageHolder holder, int position) {
final ImageInfo imageInfo = mDataList.get(position);
holder.mImageIv.setTag(imageInfo.getUri().getPath());
mPicasso.load(imageInfo.getUri())
.resize(imageInfo.getWidth(), imageInfo.getHeight())
.config(Bitmap.Config.RGB_565)
.centerCrop()
.into(new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
if(holder.mImageIv.getTag().equals(imageInfo.getUri().getPath())) { holder.mImageIv.setImageBitmap(bitmap); }}... }); }Copy the code
Before loading the image, set a tag to the ImageView, and then use the tag to determine if it is the correct ImageView after the callback. This will solve the problem of image hopping
Solve image size problem
Obtaining the size of an image is a prerequisite for efficient display of images
Get from data source
- The image is fetched from the server, which should also return the width and height of the image
- The image is obtained locally, such as the code above. Let’s see if there is an API that can query the width and height of the image
Many times, however, we don't know the size of the image in advance
Read the picture information in advance to get the width and height
In this way, after getting the Uri of the image, the width and height information of the image can be obtained through preloading.
Use Picasso as an example (this method needs to be executed in a non-main thread)
try {
Bitmap bitmap = Picasso.with(context).load(uri).get();
int width = bitmap.getWidth();
int height = bitmap.getHeight();
} catch (IOException e) {
e.printStackTrace();
}Copy the code
Normally, we don’t use this method to get the width and height of the image. For web images, preloading will download the image, but we do not know whether the user will view the image, which causes traffic waste. Also, images don’t need to be downloaded for local images, but reading and decode images can also be CPU and memory intensive. On the other hand, using Glide or Fresco is even more troublesome, as both libraries return bitmaps asynchronously and are more cumbersome to process
Dynamically calculate the picture information to get the width and height
The specific idea of this approach is recommended as follows:
- Give the ImageView a fixed width and height
- After the Bitmap is obtained, the width and height of the ImageView are recalculated based on the width and height of the Bitmap
The code in Adapter is as follows:
public void onBindViewHolder(final ImageListAdapter.ImageHolder holder, int position) {
if (mHeight == 0) return;
final ImageInfo imageInfo = mDataList.get(position);
holder.mImageIv.setImageResource(R.color.defaultImageSource);
if(imageInfo.getHeight() ! = mHeight) {if (imageInfo.getHeight() == 0) {
// set default size
imageInfo.setHeight(mHeight);
imageInfo.setWidth(mHeight);
imageInfo.setNeedResize(true);
} else {
int width = mHeight * imageInfo.getWidth() / imageInfo.getHeight();
imageInfo.setWidth(Math.min(width, mMaxWidth));
imageInfo.setHeight(mHeight);
}
}
resizeImageView(holder.mImageIv, imageInfo);
holder.mImageIv.setTag(imageInfo.getUri().getPath());
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
if (holder.mImageIv.getTag().equals(imageInfo.getUri().getPath())) {
if (imageInfo.isNeedResize()) {
// resize imageView after get bitmap info
imageInfo.setHeight(bitmap.getHeight());
imageInfo.setWidth(bitmap.getWidth());
resizeImageView(holder.mImageIv, imageInfo);
imageInfo.setNeedResize(false); } holder.mImageIv.setImageBitmap(bitmap); }}... }; mTargetMap.put(imageInfo.getUri().toString(), target); mPicasso.load(imageInfo.getUri()) .resize(imageInfo.getWidth(), imageInfo.getHeight()) .config(Bitmap.Config.RGB_565) .centerCrop() .into(mTargetMap.get(imageInfo.getUri().toString())); }private static void resizeImageView(ImageView imageView, ImageInfo imageInfo) {
ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
layoutParams.height = imageInfo.getHeight();
layoutParams.width = imageInfo.getWidth();
imageView.setLayoutParams(layoutParams);
}Copy the code
First, after finding an abnormal width and height stored in ImageInfo, set a default width and height for the ImageView, and mark the current image to recalculate the width and height. After onBitmapLoaded, if the width and height need to be calculated, extract the width and height from the bitmap, reconfigure the width and height of the ImageView and set the corresponding ImageInfo
The final result
If you have problems with Picasso, here are some solutions to find answers to some of the problems with Picasso