Search code examples
javaandroidimagepicasso

How to specify multiple fallback images with Picasso?


The Picasso library allows one to load an image easily like:

Picasso.with(context).load(url).into(imageview);

The API also allows to specify an error image. But what can I do if I want the library to first try three or four different URLs before giving up and displaying the error image? Ideally these images would be tried sequentially, falling back to the next one if the previous wasn't loaded.


Solution

  • Natively there's no API for such functionality. But with some clever coded Picasso.Target you can easily achieve such functionality.

    I'll add here a quick hack-untested code that should give you a rought idea on what to look for. You'll have to test and maybe fine tune, but that should be pretty OK.

    private static final List<MultiFallBackTarget> TARGETS = new ArrayList<MultiFallBackTarget>();
    
    public static class MultiFallBackTarget implements Picasso.Target {
    
       private WeakReference<ImageView> weakImage;
       private List<String> fallbacks;
    
       public MultiFallBackTarget(ImageView image){
          weakImage = new WeakReference<>(image);
          fallbacks = new ArrayList<String>();
          TARGETS.add(this);
       }
    
       public void addFallback(String fallbackUrl){
          fallbacks.add(fallbackUrl);
       }
    
       public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from){
    
          removeSelf();
    
          ImageView image = weakImage.get();
          if(image == null) return;
    
          image.setImageBitmap(bitmap);
       }
       public void onBitmapFailed(Drawable errorDrawable){
          ImageView image = weakImage.get();
          if(image == null) {
              removeSelf();
              return;
          }
    
          if(fallbacks.size() > 0){
             String nextUrl = fallbacks.remove(0);
             // here you call picasso again
             Picasso.with(image.getContext()).load(nextUrl).into(this);
          } else {
             removeSelf();
          }
       }
       public void onPrepareLoad(Drawable placeHolderDrawable){}
    
       private void removeSelf(){
           TARGETS.remove(this);
       }
    }
    

    Remember that Picasso does not hold strong references to the Target you put inside into(object). That means, internally Picasso uses WeakReference to that.

    So that means that you need that self reference in TARGETS to keep reference of all MultiFallBackTarget you create and allow them to self-remove when their job is done.