-
Notifications
You must be signed in to change notification settings - Fork 6.3k
Displaying Images with the Picasso Library
Displaying images is easiest using a third party library such as Picasso from Square which will download and cache remote images and abstract the complexity behind an easy to use DSL.
Adding Picasso to our app/build.gradle
file:
dependencies {
compile 'com.squareup.picasso:picasso:2.5.2'
}
Note: there is a bug with the current version of Picasso that prevents large images (i.e. 10Mb) from being remotely loaded. If you are experiencing this issue, you will need to upgrade to the Picasso 2.5.3 snapshot. See the troubleshooting to confirm.
dependencies {
compile 'com.squareup.picasso:picasso:2.5.3-SNAPSHOT'
}
We can then load a remote image into any ImageView
with:
String imageUri = "https://i.imgur.com/tGbaZCY.jpg";
ImageView ivBasicImage = (ImageView) findViewById(R.id.ivBasicImage);
Picasso.with(context).load(imageUri).into(ivBasicImage);
We can do more sophisticated work with Picasso configuring placeholders, error handling, adjusting size of the image, and scale type with:
Picasso.with(context).load(imageUri).fit().centerCrop()
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(imageView);
Be sure to use fit()
to resize the image before loading into the ImageView. Otherwise, you will consume extra memory, experience sluggish scrolling, or encounter out of memory issues if you render a lot of pictures. In addition to placeholder
and error
, there is also other configuration options such as noFade()
and noPlaceholder()
.
We can resize an image with respect to the aspect ratio using resize
and specifying 0 for the other dimension as outlined here:
// Resize to the width specified maintaining aspect ratio
Picasso.with(this).load(imageUrl).
resize(someWidth, 0).into(imageView);
We can combine resizing with certain transforms to make the image appear differently. For example, we can do a center cropping with:
Picasso.with(context).load(url).resize(50, 50).
centerCrop().into(imageView);
Transform options include centerCrop()
(Crops an image inside of the bounds), centerInside()
(Centers an image inside of the bounds), fit()
(Attempt to resize the image to fit exactly into the target). See this post for more details.
If we wish to readjust the ImageView size after the image has been retrieved, we first define a Target
object that governs how the Bitmap is handled:
private Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// Bitmap is loaded, use image here
imageView.setImageBitmap(bitmap);
}
@Override
public void onBitmapFailed() {
// Fires if bitmap couldn't be loaded.
}
}
Next, we can use the Target
with a Picasso call with:
Picasso.with(this).load("url").into(target);
You can still use all normal Picasso options like resize
, fit
, etc.
Note: The Target
object must be stored as a member field or method and cannot be an anonymous class otherwise this won't work as expected. The reason is that Picasso accepts this parameter as a weak memory reference. Because anonymous classes are eligible for garbage collection when there are no more references, the network request to fetch the image may finish after this anonymous class has already been reclaimed. See this Stack Overflow discussion for more details.
In other words, you are not allowed to do Picasso.with(this).load("url").into(new Target() { ... })
.
We can use this custom Target approach to create a staggered image view using RecyclerView
.
We first need to use DynamicHeightImageView.java that enables us to update the ImageView width and height while still preserving the aspect ratio when new images are replaced with old recycled views. We can set the ratio before the image has loaded if we already know the height:width ratio using onBindViewHolder
as shown below:
public class PhotosAdapter extends RecyclerView.Adapter<PhotoViewHolder> {
// implement other methods here
@Override
public void onBindViewHolder(PhotoViewHolder holder, int position) {
Photo photo = mPhotos.get(position);
// Set the height ratio before loading in image into Picasso
holder.ivPhoto.setHeightRatio(((double)photo.getHeight())/photo.getWidth());
// Load the image into the view using Picasso
Picasso.with(mContext).load(photo.getUrl()).into(holder.ivPhoto);
}
}
Alternatively, we can set the ratio after the bitmap has loaded if we don't know that ratio ahead of time. To avoid using an anonymous class, we will implement the Target
interface on the ViewHolder class itself for RecyclerView. When the callback is fired, we will calculate and update the image aspect ratio:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, Target {
DynamicHeightImageView ivImage;
// implement ViewHolder methods here
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// Calculate the image ratio of the loaded bitmap
float ratio = (float) bitmap.getHeight() / (float) bitmap.getWidth();
// Set the ratio for the image
ivImage.setHeightRatio(ratio);
// Load the image into the view
ivImage.setImageBitmap(bitmap);
}
}
Now the staggered grid of images should render as expected.
We can add a progress bar or otherwise handle callbacks for an image that is loading with:
// Show progress bar
progressBar.setVisibility(View.VISIBLE);
// Hide progress bar on successful load
Picasso.with(this).load(imageUrl)
.into(imageView, new com.squareup.picasso.Callback() {
@Override
public void onSuccess() {
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}
@Override
public void onError() {
}
});
You can also use this third-party library for other transformations, such as blur, crop, color, and mask.
dependencies {
compile 'jp.wasabeef:picasso-transformations:2.1.0'
// If you want to use the GPU Filters
compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
}
To do a rounded corner transformation, you would do the following:
Picasso.with(mContext).load(R.drawable.demo)
.transform(new RoundedCornersTransformation(10, 10)).into((ImageView) findViewById(R.id.image));
If you experience errors loading images, you can attach a listener to the Builder
object to print the stack trace.
Picasso.Builder builder = new Picasso.Builder(getApplicationContext());
builder.listener(new Picasso.Listener() {
@Override
public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
exception.printStackTrace();
});
Created by CodePath with much help from the community. Contributed content licensed under cc-wiki with attribution required. You are free to remix and reuse, as long as you attribute and use a similar license.
Finding these guides helpful?
We need help from the broader community to improve these guides, add new topics and keep the topics up-to-date. See our contribution guidelines here and our topic issues list for great ways to help out.
Check these same guides through our standalone viewer for a better browsing experience and an improved search. Follow us on twitter @codepath for access to more useful Android development resources.