Android Picasso - Placeholder and Error image styling
Asked Answered
I

6

41

I'm using Picasso library for image downloading from the network. I just wonder whether I can use a progress dialog or a GIF image as a place holder? Also any idea on how to make place holder image to be smaller (and fit in centre) than the actual image that is downloaded?

I tried going thru the samples but no luck. Any one here who got this to work?

Immuno answered 3/3, 2014 at 9:38 Comment(0)
C
99

You can use a placeholder image by using the *.placeholder(R.drawable.image_name)* in your Picasso call like:

Picasso.with(context)
       .load(imageUrl)
       .placeholder(R.drawable.image_name)

If you want to use a progress bar instead of a placeholder image you can create a callback inside the .into function. Do something similar to the following:

in your view:

//create the progressBar here and set it to GONE. This can be your adapters list item view for example
<RelativeLayout
    android:id="@+id/myContainer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">


    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:visibility="gone"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"/>

    <ImageView
        android:id="@+id/myImageView"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:paddingLeft="60dp"
        android:scaleType="centerCrop"></ImageView>

</RelativeLayout>

In your adapter/class:

//create a new progress bar for each image to be loaded
ProgressBar progressBar = null;
if (view != null) {
   progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
   progressBar.setVisibility(View.VISIBLE);
}

//get your image view
ImageView myImage = (ImageView) view.findViewById(R.id.myImageView);

//load the image url with a callback to a callback method/class
Picasso.with(context)
            .load(imageUrl)
            .into(myImage,  new ImageLoadedCallback(progressBar) {
                    @Override
                    public void onSuccess() {
                        if (this.progressBar != null) {
                            this.progressBar.setVisibility(View.GONE);
                         }
                    }
              });

inside your adapter/class as above I create an inner class for the callback:

private class ImageLoadedCallback implements Callback {
   ProgressBar progressBar;

    public  ImageLoadedCallback(ProgressBar progBar){
        progressBar = progBar;
    }

    @Override
    public void onSuccess() {

    }

    @Override
    public void onError() {

    }
}

Hope this makes sense!

Christenson answered 1/4, 2014 at 13:6 Comment(9)
This is just an example, instead of using the inner class for callback you can just use Callback directly as wellChristenson
What part of this actually makes a progress bar? It doesn't exist as a drawable, it is set to null so where does it exist?Behaviorism
Inside of your adapter you use an xml layout file to define the image layouts. This xml contains a ProgressBar element that is simply set to hidden/gone. Then, in your adapter you will get this ProgressBar element and assign it to a variable, the reason I assign a null value to it initially is because the assignment takes place inside an "if" clause, but the variable needs scope outside the if-clause.... If you dont initialize the variable with null it wont be initialized at all and when you try to assign the actual value your app will crash. Once it is assigned you simply set it to "visible"Christenson
Let me know if that didnt help or if you have any suggestions. Its always welcomed!Christenson
Thanks that was helpful! Is it a bad idea to create a class outside of where you are using the progress bar and passing the view, context and activity to that class so you can make all the calls(picasso etc) in that class instead of in the adapter itself?Behaviorism
Great stuff man, glad it helped. hmm, to be honest Im not 100% sure what the academically correct approach would be. I do tend to have a seperate layer/class for my networking - except for my picasso calls which I do directly in the adapter. Adapters are great and I would encourage their use. That being said, Im not saying your approach is wrong per se. See this link for some interesting performance notes: devahead.com/blog/2011/12/…Christenson
Excellent answer. Don't know why the OP did not accept it yet.Subacute
Very good. This is definitely the answer because Picasso placeHolder animationdrawable as said here by JakeWharton does not respect ScaleType on an image. github.com/square/picasso/issues/427Quiddity
I got the Callback doesn't exists.. I try to import from picasso, but my app close after open... any idea?Spigot
S
16

I implemented the progress dialog till the image download and its very simple. Take your ImageView in relative layout and place progress loader on same image view. Apply the below code and handle progress dialog visibility only.

                holder.loader.setVisibility(View.VISIBLE);
                holder.imageView.setVisibility(View.VISIBLE);
                final ProgressBar progressView = holder.loader;
                Picasso.with(context)
                .load(image_url)
                .transform(new RoundedTransformation(15, 0))
                .fit()
                .into(holder.imageView, new Callback() {
                    @Override
                    public void onSuccess() {
                        progressView.setVisibility(View.GONE);
                    }

                    @Override
                    public void onError() {
                        // TODO Auto-generated method stub

                    }
                });
            }

Hope it will help someone! :)

Synonymous answered 23/6, 2015 at 8:9 Comment(0)
A
12

Its very simple to show the progress bar.

Add this code to the activity to load the image with progress bar.

Picasso.with(this)
            .load(hoteImageStr)
            .error(R.drawable.loading)
            .into(img,  new com.squareup.picasso.Callback() {
                @Override
                public void onSuccess() {
                    if (progress != null) {
                        progress .setVisibility(View.GONE);
                    }
                }

                @Override
                public void onError() {

                }
            });

Add this to your layout file.

 <RelativeLayout
    android:id="@+id/hoteldetails_myContainer"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
   >


    <ProgressBar
        android:id="@+id/hoteldetails_progressBar"
        style="?android:attr/progressBarStyleSmall"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="center"
        android:visibility="gone"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp" />

    <ImageView
        android:id="@+id/hoteldetails_imageView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="fitXY"
        />

</RelativeLayout>
Austroasiatic answered 19/8, 2015 at 14:7 Comment(0)
F
11

first create a drawable layout like this:
progress_animation.xml:

<?xml version="1.0" encoding="utf-8"?>

<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/loading"
    android:pivotX="50%"
    android:pivotY="50%"/>


add loading.png to drawable
loading.png
Usage:

Picasso.with(context).load(imageUri).placeholder(R.drawable.progress_animation).into(imgView);
Footsie answered 19/8, 2016 at 20:26 Comment(0)
S
9

Kotlin

val circularProgressDrawable = CircularProgressDrawable(context)
circularProgressDrawable.strokeWidth = 5f
circularProgressDrawable.centerRadius = 30f
circularProgressDrawable.start()
Picasso.get().load(imageUrl).placeholder(circularProgressDrawable).into(holder.imageView)

The above code eliminates xml code and it's clean.

Samuelsamuela answered 6/1, 2019 at 1:44 Comment(1)
Use .apply { } and it's even cleaner :-)Mitzimitzie
H
1

You should setup your ProgressBar in addition to the Picasso library and you should manage the visibility of it manually using listeners.

I am using UniversalImageLoader and the Picasso library should be very similiar.

private ImageLoadingListener photoSuccess = new ImageLoadingListener() {

    @Override
    public void onLoadingStarted(String imageUri, View view) {
    }

    @Override
    public void onLoadingFailed(String imageUri, View view,
            FailReason failReason) {
        if(progress != null)
            progress.setVisibility(View.GONE);
    }

    @Override
    public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
        if(progress != null)
            progress.setVisibility(View.GONE);
        if(mImage != null)
            mImage.setVisibility(View.VISIBLE);
    }

    @Override
    public void onLoadingCancelled(String imageUri, View view) {
    }
};
Herdic answered 3/3, 2014 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.