Picasso Tutorial for Android: Getting Started
In this Picasso Tutorial, you’ll learn how to use Picasso to load images from different sources and how to apply filters and transformations. By Aldo Olivares.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Picasso Tutorial for Android: Getting Started
25 mins
Picasso is one of the most popular image caching and networking libraries for Android. It is supported by a trusted source, Square, and has several other advantages including an easy to use syntax, a ton of online resources to help you learn and several utilities like image transformations, filters and more!
In this Picasso tutorial, you will learn how to use Picasso by creating an amazing app that displays a list of popular movies from the TMDB catalog. Along the way, you’ll learn:
- How to load images from different sources.
- The difference between disk and memory caching.
- How to apply filters and transformations.
- How to set placeholders and indicators to debug your app.
Are you ready to watch some great movies? Well, grab some popcorn and let’s code!
Getting Started
In this Picasso tutorial, you will build an app called Color Movies. This app will allow you to retrieve a list of the most-popular and highest-rated movies from the TMDB API and show it in a nice grid with poster images.
Just like most public APIs, you’ll need to get an API key from TMDB to be able to make HTTP requests to it. Fortunately, getting an API key is as easy as going to https://www.themoviedb.org/settings/api and signing up using your email address and a password. Go to that URL now and log in or create an account.
Once you have your account, go to Settings ‣ API and generate an API key under the Request an API Key section. Select Developer as the type of API key, then accept the terms of use. You’ll see a form to fill out details about your application. Fill in the form with details about the app and your contact information.
Once you’ve successfully submitted the form, you can access the API Key in the same settings menu under API Key (v3 auth). Hold on to this key so you can use it in your app!
Setting Up the App
Now, download the starter project using the Download Materials button. You can find the button at the top or bottom of this tutorial. Then, open the project in Android Studio 3.3 or later by going to File ▸ Open and selecting the build.gradle file in the root package.
Once the starter project finishes loading and building, create a new file under the root directory of your app and name it keystore.properties. Your project structure should now look like this:
Inside keystore.properties add the following constant and replace everything inside the double quotes with the API key you got before:
TMDB_API_KEY="YOUR API KEY HERE"
Now sync you project with the gradle files, build and run your app to see it in action:
Sweet!
Right now it is just an empty canvas but with the power of Picasso that’s about to change. : ]
To start using Picasso, you first need to add its dependency. Open your app-level build.gradle file and add the following line to the dependencies block:
//Picasso
def picassoVersion = "2.71828"
implementation "com.squareup.picasso:picasso:$picassoVersion"
Press Sync Now and wait until Android Studio finishes syncing your project.
Basic Usage
The first thing you need to learn is how to load images using Picasso. You will see that the process is extremely easy thanks to Picasso’s syntax.
Loading Images Using Picasso
The main screen of your app is using a RecyclerView to display a series of images on a grid so you will be primarily working on its adapter.
Open MovieAdapter.kt under the ui package and add the following code inside the bind()
method of the ViewHolder
inner class:
val picasso = Picasso.get()
Picasso.get()
is a method that initializes and retrieves a global instance of Picasso with default configurations suitable for most projects.
Now, add the following code to the bottom of that same method:
picasso.load(RetrofitClient.TMDB_IMAGEURL + movie.posterPath)
.into(posterImageView)
load()
is probably Picasso’s most important method since it helps you load images from different sources using two different overrides:
-
load(uri: Uri?)
starts an image request using the specified URI and has many convenience methods such asload(path: String?)
that allows you to create the URI from the specified path. Passing a null reference as a parameter will not trigger any request but will display a placeholder image if you have specified one. -
load(resourceId: Int)
will load an image from the drawable resource ID passed as a parameter.
into()
will asynchronously insert the image into the specified ImageView once the request from load()
returns a response.
Loading the Movies
Open MainActivity.kt and add the following code inside onCreate()
:
// 1
MovieInteractor().getPopularMovies().observe(this, Observer {movieList ->
// 2
if (movieList != null) {
// 3
val adapter = MovieAdapter(movieList, windowManager) { movie ->
// 4
startActivity(
Intent(this@MainActivity, MovieDetailActivity::class.java)
.putExtra(TITLE, movie?.title)
.putExtra(SUMMARY, movie?.overview)
.putExtra(POSTER, movie?.posterPath)
.putExtra(RELEASE_DATE, movie?.releaseDate)
.putExtra(RATING, movie?.popularity)
)
}
// 5
movieRecyclerView.adapter = adapter//5
} else {
// 6
longToast("No movies")//6
}
})
Use Alt + Enter on PC or Option + Return on a Mac to import any missing dependencies, using the androidx.lifecycle.Observer
option for Observer
.
In the above code call you call getPopularMovies()
from the MovieInteractor
class to retrieve a list of movies from the TMDB API using Retrofit. Then, you create a new MovieAdapter
using list of movies and attach it to your RecyclerView. Here is a step by step explanation:
- Call the
getPopularMovies()
method of yourMovieInteractor
class. This method uses Retrofit to return a list of movies from the TMDB catalog. SincegetPopularMovies()
returns a LiveData object, you are usingobserve()
to attach anObserver
to this list and react to it as soon as there is a response. - When you get a response from the TMDB API, your LiveData object will contain either a list of movies or a null reference. If
movieList
is not null, it means you got a successful response and you can use that list to create a new adapter for yourRecyclerView
. - Create a new
MovieAdapter
using the list of movies you just received. - Here you are saying what is going to happen when the user taps on one of the images in your RecyclerView. Essentially, you are going to start a new
MovieDetailActivity
, passing anIntent
with extras that contain details of the movie selected such as the title, summary, poster, release date and rating. - Attache the adapter you just created to your
RecyclerView
. - Display a toast with a No Movies message if the response comes back null.
Build and run your App to see it in action:
The list of movies you receive might be different from the one in the screenshot above, but you now know Picasso is properly loading and inserting poster images into your RecyclerView
. Awesome!
Adding Placeholder Images
You might notice that, the first time you are loading your images, there is a brief time where your RecyclerView
shows empty items:
You get an empty list of items because you are retrieving images from a web service that takes time to respond. While this doesn’t look that bad, it’s usually considered a good practice to display a placeholder image while waiting.
Open MovieAdapter
and modify your Picasso code to add this line between the load()
and into()
calls:
.placeholder(R.drawable.iconfinder_movie_285656)
The beauty of Picasso is that you can chain different methods after load()
to modify the image you receive as a response and/or modify the behavior of your load request. By chaining placeholder()
you are telling Picasso which image you want to display while waiting.
Build and run your app to see how Picasso behaves now:
Now, instead of displaying those empty white squares, you are displaying very nice movie icons. Cool!
Resizing Images
Another thing you can do with Picasso is resizing images according to your needs. For example, right now the app looks very well on the small screen displaying a nice 2×2 grid but look at what happens if you test it on a tablet:
Obviously, your app will look different depending on the device and screen size on which you are testing it, but you still need to make sure that it looks somewhat consistent across devices. Normally, you would use resource qualifiers and create a completely different layout optimized for bigger screen sizes such as tablets or smart TVs. Since this is a Picasso tutorial, you will be resizing your images according to screen width and height.
Add the following just after val picasso = Picasso.get()
in your MovieAdapter
class:
val display = windowManager.defaultDisplay
val size = Point()
display.getSize(size)
val width = (size.x) / 2
val height = ((size.y) / 2.2).roundToInt()
The above code uses your windowManager
to calculate the width and height of the current display. After you are dividing the width by 2 and the height by 2.2 to make sure that each item in your RecyclerView occupies roughly half the screen.
Modify your Picasso code again to add this line right under placeholder()
in the chain:
.resize(width, height)
Now your images are immediately resized to roughly half the screen width and height before they are loaded into your posterImageView
.
And here is how it looks on a tablet:
Now it looks a bit more consistent among tablets and phones. Remember that you need to be very careful when resizing images since they might look extremely weird if you modify them too much due to the aspect ratios.
Resizing Placeholders
Since the movies we are retrieving are popular and famous, all of them have poster images that you can show in your app. But what happens if a movie does not have a poster image?
To test this, open MainActivity.kt and change the call to getPopularMovies()
inside onCreate()
to getSimpsonsMovies()
:
MovieInteractor().getSimpsonsMovies().observe(this, Observer {movieList ->
//...
})
Now build and run your app and scroll down to see some of the results:
The reason you might be seeing different image sizes is because resize()
only resizes images loaded as a result from the load()
method and not the placeholder image loaded with placeholder()
. One way to solve this is to “manually” assign and resize placeholder images using an if
statement.
Open MovieAdapter.kt again and replace your Picasso loading code with this:
if (movie.posterPath != null) {
picasso.load(RetrofitClient.TMDB_IMAGEURL + movie.posterPath)
.placeholder(R.drawable.iconfinder_movie_285656)
.resize(width, height)
.into(posterImageView)
} else {
picasso.load(R.drawable.iconfinder_movie_285656)
.noFade()
.resize(width, height)
.into(posterImageView)
}
Here you are adding a condition to check if the posterPath
with the URL for the image comes back null
. If it is null
, you are going to load the placeholder image into your posterImageView
and resize it with the width and height that you got before.
You might notice that you chained a different method here to your load()
statement called noFade()
. noFade()
simply disables the brief fade in animation that happens when images are being loaded from the disk, cache or network.
Build and run your app to test your changes:
Your app is becoming better and better. Sweet!
Using Transformations
Now that you know the most basic stuff you can do with Picasso, it is time to learn how to apply filters and transformations!
Open your app-level build.gradle file and add the following line to your dependencies block:
implementation 'jp.wasabeef:picasso-transformations:2.2.1'
The above line adds a nice library to your dependencies that contains many predefined filters. You can apply these filter to your images using Picasso.
Press Sync Now and wait until Android Studio finishes syncing your new dependencies.
Now open MovieDetailActivity.kt and add the following at the top of your class:
private val picasso = Picasso.get()
Since you are going to be using Picasso in many places inside this class, it would be useful to have a property with a reference to it.
Now add the following method:
private fun configureUI() {
// 1
titleDetailTextView.text = intent.getStringExtra(MainActivity.TITLE)
summaryDetailTextView.text = intent.getStringExtra(MainActivity.SUMMARY)
releaseDateTextView.text = intent.getStringExtra(MainActivity.RELEASE_DATE)
ratingTextView.text = String.format(getString(R.string.rating),
intent.getFloatExtra(MainActivity.RATING, 1f).roundToInt())
// 2
picasso
.load(RetrofitClient.TMDB_IMAGEURL +
intent.getStringExtra(MainActivity.POSTER))
.error(R.drawable.iconfinder_movie_285656)
.into(detailImageView)
}
The above method:
- Configures your TextViews using the information received from the extras in your
Intent
. - Loads an image into your
detailImageView
using Picasso like you previously did.
You might have noticed that you are using error()
instead of placeholder()
here. error()
is used exclusively to load a drawable resource when the requested image can’t be loaded.
Now add a call configureUI()
inside onCreate()
to immediately load your image when the activity is started:
configureUI()
Build and run your app and tap on movie item just to verify that everything is working properly:
Applying Blur and Grayscale Transformations
If you select one of the options in the action bar menu at the top you might notice that nothing happens, but don’t worry that’s about to change.
Add the following code inside the MovieDetailActivity
‘s setBlurFilter()
:
picasso
.load(RetrofitClient.TMDB_IMAGEURL +
intent.getStringExtra(MainActivity.POSTER))
.transform(BlurTransformation(this))
.error(R.drawable.iconfinder_movie_285656)
.into(detailImageView)
The above uses the same code you already know to load an image and insert it into detailImageView
. The only difference here is that your are now using the transform()
method to apply a blur filter to your image. BlurTransformation()
is a Transformation
subclass that has already been defined in the library that you imported at the beginning of this section.
Ever wondered how the poster images would look in grayscale? Well, this library also includes a transformation that you can use to achieve this.
Add the following code inside setGrayScale()
:
picasso
.load(RetrofitClient.TMDB_IMAGEURL +
intent.getStringExtra(MainActivity.POSTER))
.transform(GrayscaleTransformation())
.error(R.drawable.iconfinder_movie_285656)
.into(detailImageView)
Build and run your app and tap on any image:
Now, use the action bar menu and select Blur:
How cool is that? Now, select Grayscale on the menu and see what happens:
Great! That’s a nice set of filters. :]
Applying Custom Transformations
While this library contains a wide set of filters that you can use, it is also fun to know how to define your own filters in case you need it in the future.
Create new package under the colormovies directory (com.raywenderlich.colormovies) and name it utils. Inside this package create a new class and name it CropSquareTransformation
.
Modify CropSquareTransformation
to implement the Transformation
interface:
class CropSquareTransformation : Transformation {
}
Transformation
interface that comes in the com.squareup.picasso package.Now, override transform()
:
override fun transform(source: Bitmap): Bitmap {
val size = Math.min(source.width, source.height)
val x = (source.width - size) / 2
val y = (source.height - size) / 2
val result = Bitmap.createBitmap(source, x, y, size, size)
if (result != source) {
source.recycle()
}
return result
}
This is a simple transformation that crops your image so it looks like a square by reducing the size of either the width or the height.
Finally, override key()
:
override fun key(): String {
return "square()"
}
Here you need to return a unique key that represents your transformation. This is used by Picasso for caching purposes.
To use your new transformation open MovieDetailActivity.kt and add the following code to setCropSquareTransformation()
:
picasso
.load(RetrofitClient.TMDB_IMAGEURL +
intent.getStringExtra(MainActivity.POSTER))
.transform(CropSquareTransformation())
.error(R.drawable.iconfinder_movie_285656)
.into(detailImageView)
Build and run your app, go to Movie Details and select CropSquare on the menu to see your new transformation in action:
Debugging with Picasso
By now you already know about most of the features available to you with Picasso, but there is one thing remaining: debugging.
Picasso offers several methods to prioritize requests and to help you understand how an image was loaded.
Open MovieAdapter.kt and add the following line just below val picasso = Picasso.get()
:
picasso.setIndicatorsEnabled(true)
The above method activates debugging indicators on images. From now on, you will see small ribbons on the top left corner of each image loaded by Picasso.
Build and run your app to see how debugging indicators look:
Understanding Ribbon Colors
If you have been following along until this point, you will probably see a small blue ribbon on the top left corner of each image. This means that Picasso already downloaded and saved those pictures and didn’t need to make an HTTP request and load them. There are three possible colors for the debugging indicators and each represents a different source for your image:
- Red: Loaded from the network. This is the slowest possible way to load an image since you need to wait for a response from a URL endpoint.
- Blue: Loaded from the disk. As previously mentioned, this means that Picasso had already downloaded and saved those pictures on your memory.
- Green: Loaded from the cache. This is the fastest way to load an image since you are using your device’s cache memory to load and display your drawable resource.
If you want to play around with debugging indicators try scrolling down and see what happens:
The images at the bottom will probably have a red ribbon if this is the first time that you see them.
Now, scroll up again and take a look at the first images that you saw:
A green ribbon! Those images were still in your device’s cache memory.
Don’t worry if your debugging indicators look a bit different. Remember, it is entirely possible that an image had to be loaded from the disk or network again due to the operating system clearing some memory for other apps. Picasso’s caching system could also think that you need a new copy of your image. Just take a look at the first time I tried to take a screenshot for this tutorial:
The first image was loaded from my device’s cache memory while the other ones were loaded from the disk. Again, don’t worry too much about this and let Picasso handle memory management for you.
Managing Memory
You can also specify which memory and network policies you want to use for each of your Picasso requests.
Open MovieAdapter.kt and add the following calls to your chain to load an image from a URL:
// 1
.networkPolicy(NetworkPolicy.NO_CACHE)
// 2
.memoryPolicy(MemoryPolicy.NO_STORE)
// 3
.memoryPolicy(MemoryPolicy.NO_CACHE)
These are some new methods you’re using, so to walk through them:
-
networkPolicy()
specifies theNetworkPolicy
for this request.NetworkPolicy.NO_CACHE
skips checking the disk cache and forces loading through the network. -
memoryPolicy()
specifies theMemoryPolicy
for this request.MemoryPolicy.NO_STORE
skips storing your image into your device’s memory cache. -
memoryPolicy(MemoryPolicy.NO_CACHE)
skips memory cache lookup when loading an image.
Remember that your new memory policies won’t affect previously stored images. So, before testing your new changes, clear the cache and data for this app by going to the app info menu and selecting clear cache and data:
You could also completely delete and reinstall the app if you wish.
Build and run the app again:
You should see that all your images will have a red ribbon since you have specified that you don’t want to store any images and want to force loading through the network each time.
Where to Go From Here?
Congratulations! You can now use your Color Movies app to get a list of the most popular movies out there and have a good time with your friends. :]
Feel free to download the completed project to check it out using the the Download Materials button found at the top or bottom of this tutorial.
By now you should have a very good understanding of how Picasso can help you work with images on Android by providing caching, filters, transformations and more.
If you want to learn about other image downloading caching libraries, I suggest you take a look at Glide, a very popular and powerful alternative to Picasso. You may also check out Glide Tutorial at our site.
Another good alternative to Picasso and Glide is Universal Image Downloader which is currently the #1 Android Library on Github at the time of this writing.
If you have any questions or comments, please drop them in the comments section below or in the forums.
Happy coding!