Hassle-Free Image Loading in Android with Picasso from Square
Images add flavor, character and delight to your app interfaces. Image loading in Android typically involves adding many lines of code boilerplate code. Picasso from Square is one of the simplest libraries to use and includes memory and data optimization, allowing you to choose not to save the images in cache after they are downloaded.
In this tutorial I will show how to download images from external locations, using a file path, image resource id or any URI in your Android application using Picasso.
You can find the project on GitHub. You can download the drawables used in the project here, make sure you add them to the drawables folder.
Adding Picasso to Your Project
Note: I am using version 2.5.2 of Picasso.
Create a new Project in Android Studio and add this line inside dependencies
in build.gradle (Module: app):
compile 'com.squareup.picasso:picasso:2.5.2'
Sync Gradle in your project to download the library.
Add the internet permission in AndroidManifest.xml.
<uses-permission android:name="android.permission.INTERNET"/>
In this project I am going to use a single activity class and layout resource file.
XML Layout
The interface of this sample application includes three ImageView
s, inside a vertical-oriented linear layout. Each image will be downloaded, saved and displayed using a different method of the library.
The code for activity_main.xml
is below (don’t forget to change to your package name):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.theodhor.picasso.MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/activity_main"
android:layout_centerHorizontal="true">
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center_horizontal"
android:id="@+id/image_1"
android:layout_margin="5dp"/>
<ImageView
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_gravity="center_horizontal"
android:id="@+id/image_2"
android:layout_margin="5dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image_3"
android:layout_margin="5dp"/>
</LinearLayout>
</RelativeLayout>
Displaying Images
The first step is creating a reference in your Java code for each of the above ImageViews
. Add the following below the MainActivity
class declaration:
ImageView firstImage, secondImage, thirdImage;
Below this code, create String
variables and give them the value of any image URI. Since you are using three ImageView
s, an URI for each is needed:
private static final String IMAGE_1 =
"http://uxmastery.com/wp-content/uploads/2015/06/sitepoint-logo-195x195.jpg";
private static final String IMAGE_2 =
"http://uxmastery.com/wp-content/uploads/2015/06/sitepoint-logo-195x195.jpg";
private static final String IMAGE_3 =
"https://www.sitepoint.com/premium/books/git-fundamentals/online/images/sitepoint-logo-2012.png";
Inside the onCreate()
method, create the references for the ImageViews
:
firstImage = (ImageView)findViewById(R.id.image_1);
secondImage = (ImageView)findViewById(R.id.image_2);
thirdImage = (ImageView)findViewById(R.id.image_3);
The simplest way to download an image, save it in device memory and display it is with this line of code, added below the image references:
Picasso.with(this).load(IMAGE_1).centerCrop().fit().into(firstImage);
You need to reference each instance of Picasso inside the application context, using with(context)
. Then .load(parameter)
can accept a file
, resource id
, path
or uri
of any image as a parameter. .centerCrop()
places the image in the center of the ImageView
and uses .fit()
to stretch the image according to the ImageView
size. You can also use .resize(int width, int height)
to set the explicit size of the downloaded image. The last method, .into(parameter)
, accepts the ImageView
to display the image as a parameter.
This was the shortest line of code for displaying an image from an external source.
This is the shortest line of code to load an image, but Picasso lets you use different methods to better use network data, device memory and processor.
A more customized example to do the same is:
Picasso.with(this)
.load(IMAGE_1)
.centerCrop()
.fit() .placeholder(getResources().getDrawable(R.drawable.placeholder,null))
.error(getResources().getDrawable(R.drawable.no_image,null))
.rotate(45)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.priority(Picasso.Priority.HIGH)
.into(firstImage);
With larger images, you can use a placeholder
which displays a dummy image until the desired one has downloaded and ready to display. You can use the same technique to avoid empty ImageView
s when loading encounters an error.
By skipping Cache
, Picasso avoids cache look up while processing a request. This can apply in cases when you are sure that the image cannot be found in cache. Use .memoryPolicy(MemoryPolicy.NO_STORE)
to not store the image in cache after it has downloaded.
Each image can have its own Priority
: LOW
, NORMAL
or HIGH
.
You are going to display the secondImage
using a Target
which has three main methods to determine the status of the image downloading process. Add this code after the previous Picasso calls:
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
secondImage.setImageBitmap(bitmap);
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
secondImage.setImageDrawable(errorDrawable);
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) { secondImage.setImageDrawable(getResources().getDrawable(R.drawable.prepare_load,null));
}
};
target.onBitmapFailed(getResources().getDrawable(R.drawable.no_image,null));
Picasso.with(this).load(IMAGE_2).priority(Picasso.Priority.HIGH).into(target);
The overridden methods of the Target
allow you to better manage the process. You can set the parameters of these methods by calling them from outside of the target
. You can set the bitmap
displayed when downloading fails using:
target.onBitmapFailed(getResources().getDrawable(R.drawable.no_image,null));
The next line creates an instance of Picasso, downloading the image located at IMAGE_2.
You will download the third image using a ‘Picasso Builder’. This will help distinguish where this image comes from. If the image is found in device memory or disk, Picasso won’t download it but use the currently available one. By creating a Builder, you can display Debug Indicators that show the image source.
The builder supports another method, called when an image load fails.
//Third method
Picasso.Builder picassoBuilder = new Picasso.Builder(this);
picassoBuilder.listener(new Picasso.Listener() {
@Override
public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
Log.e("TAG","Failed");
}
});
Picasso picasso = picassoBuilder.build();
picasso.setLoggingEnabled(true);
picasso.setIndicatorsEnabled(true);
picasso.load(IMAGE_3).centerCrop().resize(400,100).placeholder(R.drawable.no_image).into(thirdImage);
This time you are creating an instance of Picasso using the Builder. As this image is wide, you need to resize the image to keep the correct ratio. A placeholder can be a good element for the application user interface because we can never predict the connection speed and time required to download each image.
Conclusion
In this tutorial I showed you how easy the Picasso library is to configure and use allowing you to download, display and format images in a few lines of code.
I hope you find it as easy to use as I do and if you have any comments or questions, please let me know below.