116

I have the following code to load an image in Picasso, using a drawable for the placeholder to display while the image is downloading. What I want though is an animated spinning progress bar style spinner that animates around and around while the image is loading, like I see in most professional apps. Picasso doesn't seem to support this, only static image drawables. Is there a way to get it working with Picasso or do I have to do something different?

Picasso.with(context).load(url)             
                    .placeholder(R.drawable.loading)
                    .error(R.drawable.image_download_error)
                    .into(view);
NZJames
  • 4,963
  • 15
  • 50
  • 100

12 Answers12

270

How to have a loading progress animation image using Picasso placeholder:

I solved this easily using a animated-rotate xml object.

Steps:

progress_image.png

progress_image.png

/res/drawable/progress_animation.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

<item android:gravity="center">
    <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/progress_image"
        android:pivotX="50%"
        android:pivotY="50%" />
    </item>
</layer-list>

Picasso loading:

Picasso.with( context )
        .load( your_path )
        .error( R.drawable.ic_error )
        .placeholder( R.drawable.progress_animation )
        .into( image_view );
DBragion
  • 3,651
  • 2
  • 16
  • 12
  • Yes! I use this for a long time, and now, looking for a oficial documentation about animated-rotate drawables, a didn't find anything. Did you tested this solution? It's has always worked for me. – DBragion Mar 19 '15 at 11:40
  • No I haven't tested it yet, because it looked suspicious for me, but if it works it will be cool solution. – Mohammad Ersan Mar 19 '15 at 11:45
  • It's not working. Image stays still. I also used my [code](http://stackoverflow.com/a/27832916/3531756) for rotation but still nothing. – Tushar Gogna Mar 31 '15 at 09:05
  • 33
    @DBragion it works great but for large images, it scalled up. can we fix width and height of it to a number like 32x32 pixels? – m3hdi Jun 15 '15 at 10:11
  • Not working. @MohammedSubhiSheikhQuroush how did you make it work? Any special version of Android? – noloman Oct 24 '15 at 11:43
  • Simple solution but I will try to find better solution. User wont be happy seeing loading indicator that is huge. – Vlado Pandžić Dec 30 '15 at 14:09
  • 10
    This doesn't work, no animation shown. Just a plain image – Joe Maher Feb 05 '16 at 06:04
  • 1
    doesn't animate just a static image – Abhimaan Feb 09 '16 at 11:19
  • 1
    Working. Image rotating. Only problem was image bit big. I used photoshop to scale it down. Is there any other android way to scale it down? – Gereltod Mar 09 '16 at 03:20
  • I am afraid you didn't specify height and width , that shows an error in android studio – geniushkg Apr 30 '16 at 08:03
  • you can use this https://romannurik.github.io/AndroidAssetStudio/icons-generic.html to scale the icon. – aLIEz Nov 05 '16 at 04:12
  • Works fairly well for me. – Akah Feb 21 '17 at 11:03
  • @Vlado did you find a solution? – usernotnull Mar 28 '17 at 14:51
  • 4
    It is working but I want small loader not as my imageview size loader – Ganpat Kaliya May 06 '17 at 06:18
  • 2
    Worked fine, the animation stays small until few milliseconds before the downloaded image appear, it gets maximized and stretched for few milliseconds then the image appear. Is there any solution? – tinyCoder May 23 '17 at 21:31
  • 2
    Use http://romannurik.github.io/AndroidAssetStudio/index.html (select "Launcher icon generator") to create mdpi, hdpi, xhdpi, etc. – CoolMind Jan 10 '18 at 08:40
  • Please be sure to use always the latest version of Picasso or the animation won't work as it should be. The min version goes from: compile 'com.squareup.picasso:picasso:2.5.2' to newer versions. – Luis Cardoza Bird May 18 '19 at 15:44
  • 1
    @LuisCardozaBird Na, it doesn't work with latest one either. – Tushar Gogna Oct 11 '19 at 05:50
  • My experience with this solution caused Picasso `RequestCreator.getPlaceholderDrawable()` to crash on Android versions of Marshmallow (6.xx). Not sure why but it doesn't seem to like the `animated-rotate` inside a `drawable` as a placeholder. – jds17 Mar 19 '20 at 18:11
  • Works great with Glide too. Thanks a lot. – sonudelhikkc May 16 '20 at 07:27
77

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

                }
            });
        }

Enjoy. :)

Akanksha Rathore
  • 3,603
  • 3
  • 31
  • 47
  • 7
    Actually you should pay attention to weak references and declare a **Callback object** in this way **in order to avoid garbage collection** (otherwise onSuccess could never get called): `final Callback loadedCallback = new Callback() { @Override public void onSuccess() { // your code } @Override public void onError() { // your code } }; imageView.setTag(loadedCallback); Picasso.with(context).load(url).into(imageView, loadedCallback);` – vvz3n Jan 28 '16 at 11:24
  • Anyone confused by the lingo, a Progress Loader is a Progress Bad – Joe Maher Feb 05 '16 at 06:11
  • 2
    You missed to add RoundedTransformation class. This class is here https://gist.github.com/aprock/6213395 – Md. Sajedul Karim Apr 25 '16 at 08:08
  • Not helpful if there are multiple images. Makes the UI complex for no reason. – Tushar Gogna Oct 11 '19 at 05:52
5

Picasso doesn't support animated placeholders unfortunately.

One way you could work around this is to place your ImageView in a FrameLayout with the animated drawable underneath. This way when Picasso loads the image it will load over the top of the animated placeholder, giving the user the intended effect.

Alternatively, you could load the image into a Target. Then you'd have the progress bar showing by default, and when the onBitmapLoaded method is called you can hide it and display the image. You can see a basic implementation of this here

Community
  • 1
  • 1
Michael
  • 1,194
  • 2
  • 10
  • 19
  • 3
    Picasso *does* support animated placeholders. You write up the animation drawable file (includes animation list with several items representing each image in the animation). You feed that file to a new AnimationDrawable object and then pass it to placeHolder() in the Picasso loader constructor. – Odaym Jun 13 '15 at 13:47
4

I've tried many ways on the stack overflow and finally found a way to do it is using "CircularProgressDrawable". Ref: https://developer.android.com/reference/androidx/swiperefreshlayout/widget/CircularProgressDrawable

Kotlin code sample

fun Context.loadImage(view: ImageView, url: String) {
  val circularProgressDrawable = CircularProgressDrawable(this)
  circularProgressDrawable.apply {
    strokeWidth = 5f
    centerRadius = 30f
    setColorSchemeColors(
      getColor(Color.WHITE)
    )
    start()
  }

  Picasso
    .get()
    .load(url)
    .fit()
    .centerInside()
    .placeholder(circularProgressDrawable)
    .into(view)
}
ukhome
  • 41
  • 2
1

Just add shape attribute in DBragion's answer as like below and it will work like charm. Happy coding.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>

        <shape
            android:shape="rectangle">

            <size
                android:width="200dp"
                android:height="200dp"/>

            <solid
                android:color="#00FFFFFF"/>
        </shape>
    </item>

    <item android:gravity="center">
        <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
            android:drawable="@drawable/spinner"
            android:pivotX="50%"
            android:pivotY="50%" />
    </item>
</layer-list>

You can use Glide too:

Glide.with(activity).load(url)
                    .apply(RequestOptions.centerInsideTransform())
                    .placeholder(R.drawable.progress_animation)
                    .into(imageview);
Tushar Lathiya
  • 940
  • 9
  • 26
1

I found answer to this question!

See: https://github.com/square/picasso/issues/427#issuecomment-266276253

In addition to answer of @DBragion, try below.

Now we can fix height and width!

image_view.scaleType = ImageView.ScaleType.CENTER_INSIDE
picasso.load(your_path)
        .fit()
        .centerCrop()
        .noFade()
        .placeholder(R.drawable. progress_animation)
        .into(image_view)

I think there are two key points.

  1. use noFade()

  2. set image_view's scaleType to "CENTER_INSIDE"

1
   // create a ProgressDrawable object which we will show as placeholder
    CircularProgressDrawable progress = new CircularProgressDrawable(context);
    progress.setColorSchemeColors(R.color.colorPrimary, R.color.colorPrimaryDark, R.color.colorAccent);
    progress.setCenterRadius(30f);
    progress.setStrokeWidth(5f);
    progress.start();

Picasso.with(context).load((String) chatMessage.getPath()).error(R.drawable.error)
                    .placeholder(progress)
                    .noFade()
                    .into(viewHolder.imageBody);
Yasin Ege
  • 605
  • 4
  • 14
0

I made it working in following way:

  1. Created a drawable (found somewhere on the Internet) and put it in the res/drawable:

    <?xml version="1.0" encoding="utf-8"?>
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="90"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360">
    
    <shape
        android:innerRadiusRatio="3"
        android:shape="ring"
        android:thicknessRatio="7.0">
    
        <gradient
            android:angle="0"
            android:centerColor="#007DD6"
            android:endColor="#007DD6"
            android:startColor="#007DD6"
            android:type="sweep"
            android:useLevel="false" />
    </shape>
    

  2. In my item for the GridView added ProgressBar element:

    <ProgressBar
        android:id="@+id/movie_item_spinner"
        android:layout_width="@dimen/poster_width"
        android:layout_height="@dimen/poster_height"
        android:progressDrawable="@drawable/circular_progress_bar"/>
    
  3. In Adapter added Target Object with following parameters, where poster and spinner are references to ImageView and ProgressBar:

    // Target to show/hide ProgressBar on ImageView

    final Target target = new Target() {
    
            @Override
            public void onPrepareLoad(Drawable drawable) {
                poster.setBackgroundResource(R.color.white);
                spinner.setVisibility(View.VISIBLE);
            }
    
            @Override
            public void onBitmapLoaded(Bitmap photo, Picasso.LoadedFrom from) {
                poster.setBackgroundDrawable(new BitmapDrawable(photo));
                spinner.setVisibility(View.GONE);
            }
    
            @Override
            public void onBitmapFailed(Drawable drawable) {
                poster.setBackgroundResource(R.color.white);
            }
        };
    
  4. Results will be like that on loading - circle is rotating (sorry for this screenshot, but images are appearing too fast): ScreenShot

Dmytro Karataiev
  • 1,214
  • 1
  • 14
  • 19
0

For anyone trying to use DBragion's technique: Make sure you have the latest version of Picasso or else it wont spin. Mine didn't work until I used version 2.5.2.

compile 'com.squareup.picasso:picasso:2.5.2'
MJH
  • 2,301
  • 7
  • 18
  • 20
0

In this link, Jake Wharton said :

Nope. We use the Android BitmapFactory for all image decoding and it has no animated GIF support (sadly).

Then you can't

Radesh
  • 13,084
  • 4
  • 51
  • 64
0

Just use Picasso PlaceHolder

  Picasso.get()
        .load(datas[position].artist?.image)
        .placeholder(R.drawable.music_image)
        .error(R.drawable.music_image)
        .into(holder.cardViewImage)
Exel Staderlin
  • 535
  • 7
  • 10
0

@DBragion's answer was great. The problem with it (like many mentioned in the comments) was it didn't provide a way to specify the height / width of the progress indicator. Following is the modified version of his answer with combination of @AkankshaRathod's answer.

loading_indicator.png

Loading Indicator Image

progress_animation.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:gravity="center">
        <animated-rotate
            android:drawable="@drawable/loading_indicator"
            android:pivotX="50%"
            android:pivotY="50%" />
    </item>
</layer-list>

CardView (or any container Layout) containing your ImageView

<androidx.cardview.widget.CardView
    android:id="@+id/vCategoryItem"
    android:layout_width="wrap_content"
    android:layout_height="@dimen/height_category_image"
    android:layout_marginLeft="@dimen/smaller"
    app:cardCornerRadius="@dimen/small">

    <ImageView
        android:id="@+id/imgCategoryImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:scaleType="centerCrop"
        android:src="@drawable/demo" />

    <ImageView
        android:id="@+id/loadingIndicator"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:layout_gravity="center"
        android:layout_centerInParent="true"
        android:src="@drawable/progress_animation" />

    <TextView
        android:id="@+id/lblCategoryName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_gravity="bottom"
        android:layout_marginBottom="@dimen/big"
        android:ellipsize="marquee"
        android:gravity="center"
        android:paddingLeft="@dimen/big"
        android:paddingRight="@dimen/big"
        android:singleLine="true"
        android:text="Category Name"
        android:textColor="@android:color/white"
        android:textSize="15sp"
        android:textStyle="bold" />

</androidx.cardview.widget.CardView>

Picasso loading:

Picasso.get().load(yourImageURL).into(imgCategoryImage, new Callback() {
    @Override
    public void onSuccess() {
        loadingIndicator.setVisibility(View.GONE);
    }

    @Override
    public void onError(Exception e) {

    }
});
Dharman
  • 30,962
  • 25
  • 85
  • 135
Mahendra Liya
  • 12,912
  • 14
  • 88
  • 114