Picasso — Placeholders, Errors, and Fading

After we demonstrated the basic networking, loading and caching capabilities of Picasso in the last three weeks, it's time to move to more advanced topics. This week, we'll cover all kinds of placeholders. Picasso does not only display images, it also got your back until the mentioned image is loaded; or if it can't be loaded at all.

Picasso Series Overview

Placeholder: .placeholder()

We probably don't even have to explain or discuss it: empty ImageViews don't look good in any UI. If you're using Picasso, you most likely are loading images via an internet connection. Depending on your users' environment, this might take a significant amount of time. An expected behavior of an application is to display a placeholder until the image is loaded and processed.

Picasso's fluent interface makes this very easy to do! Just call .placeHolder() with a reference to a drawable (resource) and Picasso will display that as a placeholder until your actual image is ready.

Picasso  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher) // can also be a drawable
    .into(imageViewPlaceholder);

For obvious reasons, you cannot set an Internet url as placeholder, since that one would be required to be loaded as well. App resources and drawables are guaranteed to be available and accessible. However, for a the load() parameter, Picasso accepts all kind of values. These might not be loadable (no Internet connection, server down, ...), deleted or not accessible. In the next section, we'll talk about an error placeholder.

Error Placeholder: .error()

Let's assume our app tries to load an image from a website, which is currently down. Picasso does give us the option to get an error callback and take the appropriate action. While we'll cover that option later, for now this would be too complicated. In most use cases a placeholder, which signals that the image could not be loaded is sufficient enough.

The call to Picasso's fluent interface is identical to the previous example for our pre-display placeholder, just with a different function call named error():

Picasso  
    .with(context)
    .load("http://futurestud.io/non_existing_image.png")
    .placeholder(R.mipmap.ic_launcher) // can also be a drawable
    .error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded
    .into(imageViewError);

That's it. If the image you define as load() value cannot be loaded, Picasso will display R.mipmap.future_studio_launcher instead. Once again, acceptable parameters for error() are only already initialized drawable objects or pointers to their resources (R.drawable.<drawable-keyword>).

Use of noFade()

No matter if you're displaying a placeholder before loading the image or not, Picasso automatically fades the image into the ImageView to soften the significant change in your UI. If you wish to directly display the image without the small fade effect, call .noFade() on the Picasso object:

Picasso  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher) // can also be a drawable
    .error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded
    .noFade()
    .into(imageViewFade);

This would directly show you the image, without fading it into the ImageView. Please make sure you've a good reason for doing this though!

It is important to know that all these parameters are independent and can be set without relying on each other. For example, you could just set .error() without calling .placeholder(). Any combination of the parameters is possible.

Use of noPlaceholder()

Lastly, you might find a little gem called .noPlaceholder() in the documentation. It's important to understand, that this does not disable the previously set placeholders via .placeholder() or .error()! It covers a different use case.

Let's think about the following scenario: you want to load an image into an ImageView, and after some time you want to load a different image into the same ImageView. With the default configuration, the moment you create the second Picasso call, the ImageView will be cleared by the previous image and the placeholder set by .placeholder() will be displayed. This can look ugly if the ImageView is prominent in your UI and the user notices the possible rapid changes between images within seconds. A better solution is to call .noPlaceholder() in the second Picasso request. This will keep the previous image in place until the second one is loaded. It results in a much smoother experience for your user.

// load an image into the imageview
Picasso  
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher) // can also be a drawable
    .into(imageViewNoPlaceholder, new Callback() {
        @Override
        public void onSuccess() {
            // once the image is loaded, load the next image
            Picasso
                .with(context)
               .load(UsageExampleListViewAdapter.eatFoodyImages[1])
               .noPlaceholder() // but don't clear the imageview or set a placeholder; just leave the previous image in until the new one is ready
               .into(imageViewNoPlaceholder);
        }

        @Override
        public void onError() {

        }
    });

This small code snippet will do exactly what we just described. Immediately after finishing loading the first image, it'll start the second request. However, thanks to the .noPlaceholder() call, it'll keep the previous image in place.

Side note: if you're confused by the .into(imageView, callback), don't worry, we'll explain that in a later post.

Outlook

Hopefully you understood and learned a lot from this blog post. It is tremendously important for a good user experience that the images are not popping in unexpectedly. Also, make it obvious to the user when something goes wrong. Picasso assists you with easy to call functions, which provide the things you need to have a better app.

But we're not done with our optimization yet. Next week, we'll look at image resizing, scaling and a wonderful function named .fit().

Explore the Library

Find interesting tutorials and solutions for your problems.