[TUT] ImageView with Loading Spinner

Hi guys, read a post where someone wanted to load a url image in an ImageView and show a loading spinner whilst this was happening. So I thought it would be a fun exercise for me to do.

I originally posted this here: AndDev but have now migrated to my own blog.

Ok so i’ve implemented a View that shows a loading spinner whilst it is retrieving an image from the web.
This is useful when people have slow http connections and you at least want to show them that your loading something!

To start with you will need:

     <uses-permission android:name="android.permission.INTERNET" />

in your manifest.

Ok this is the ImageView Object, it is mostly self explanatory, just add it to your project package:

import java.io.IOException;
import java.net.MalformedURLException;
 
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.Handler.Callback;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
 
/**
 * Free for anyone to use, just say thanks and share
 * @author Blundell
 *
 */
public class LoaderImageView extends LinearLayout{
 
        private static final int COMPLETE = 0;
        private static final int FAILED = 1;
 
        private Context mContext;
        private Drawable mDrawable;
        private ProgressBar mSpinner;
        private ImageView mImage;
       
        /**
         * This is used when creating the view in XML
         * To have an image load in XML use the tag 'image="https://developer.android.com/images/dialog_buttons.png"'
         * Replacing the url with your desired image
         * Once you have instantiated the XML view you can call
         * setImageDrawable(url) to change the image
         * @param context
         * @param attrSet
         */
        public LoaderImageView(final Context context, final AttributeSet attrSet) {
                super(context, attrSet);
                final String url = attrSet.getAttributeValue(null, "image");
                if(url != null){
                        instantiate(context, url);
                } else {
                        instantiate(context, null);
                }
        }
       
        /**
         * This is used when creating the view programatically
         * Once you have instantiated the view you can call
         * setImageDrawable(url) to change the image
         * @param context the Activity context
         * @param imageUrl the Image URL you wish to load
         */
        public LoaderImageView(final Context context, final String imageUrl) {
                super(context);
                instantiate(context, imageUrl);        
        }
 
        /**
         *  First time loading of the LoaderImageView
         *  Sets up the LayoutParams of the view, you can change these to
         *  get the required effects you want
         */
        private void instantiate(final Context context, final String imageUrl) {
                mContext = context;
               
                mImage = new ImageView(mContext);
                mImage.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
               
                mSpinner = new ProgressBar(mContext);
                mSpinner.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
                       
                mSpinner.setIndeterminate(true);
               
                addView(mSpinner);
                addView(mImage);
               
                if(imageUrl != null){
                        setImageDrawable(imageUrl);
                }
        }
 
        /**
         * Set's the view's drawable, this uses the internet to retrieve the image
         * don't forget to add the correct permissions to your manifest
         * @param imageUrl the url of the image you wish to load
         */
        public void setImageDrawable(final String imageUrl) {
                mDrawable = null;
                mSpinner.setVisibility(View.VISIBLE);
                mImage.setVisibility(View.GONE);
                new Thread(){
                        public void run() {
                                try {
                                        mDrawable = getDrawableFromUrl(imageUrl);
                                        imageLoadedHandler.sendEmptyMessage(COMPLETE);
                                } catch (MalformedURLException e) {
                                        imageLoadedHandler.sendEmptyMessage(FAILED);
                                } catch (IOException e) {
                                        imageLoadedHandler.sendEmptyMessage(FAILED);
                                }
                        };
                }.start();
        }
       
        /**
         * Callback that is received once the image has been downloaded
         */
        private final Handler imageLoadedHandler = new Handler(new Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                        switch (msg.what) {
                        case COMPLETE:
                                mImage.setImageDrawable(mDrawable);
                                mImage.setVisibility(View.VISIBLE);
                                mSpinner.setVisibility(View.GONE);
                                break;
                        case FAILED:
                        default:
                                // Could change image here to a 'failed' image
                                // otherwise will just keep on spinning
                                break;
                        }
                        return true;
                }              
        });
 
        /**
         * Pass in an image url to get a drawable object
         * @return a drawable object
         * @throws IOException
         * @throws MalformedURLException
         */
        private static Drawable getDrawableFromUrl(final String url) throws IOException, MalformedURLException {
                return Drawable.createFromStream(((java.io.InputStream)new java.net.URL(url).getContent()), "name");
        }
       
}

An example of instantiation within XML would look like this, changing my package name to wherever you have put the LoaderImageView class file:

<com.blundell.tut.LoaderImageView
   android:id="@+id/loaderImageView"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   image="https://developer.android.com/images/dialog_buttons.png"
   />

And an example of programmatic instantiation looks like this:

final LoaderImageView image = new LoaderImageView(this, "https://developer.android.com/images/dialog_buttons.png");
        image.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

I’ve also attached the example project, which has both types of ImageView class’s so refer to that if you need a bit more background.

Remember to say thanks 🙂 and any questions just ask!

—-> Download Image Loader Src Here <----

38 thoughts on “[TUT] ImageView with Loading Spinner

  1. Hi…The image does not load…the progressbar is loading loading keep loading. Am I missing anything?

    1. Yes. Do you have the internet permission in your manifest? Check your logcat for errors

    2. Yes I have Internet permission in manifest file.In logcat there is no errors. I have to load the image via code not in xml. I have set width and height in XML for that ImageView. Shoud I set layout Layout params in code too?

    3. I have tried your example project. In that too image does not load, progress bar is loading loading … 🙁

  2. Hey there, a lot of thanks to you! Your class is just awesome!

    But I’ve got 1 problem:
    My Layout is defined in XML, and I want the downloaded Image matching the width of the screen.

    In my Relative Layout (with fill_parent attribute) I define it like this:

    In the Activity I call it this way:
    image = (LoaderImageView) findViewById(R.id.loaderImageView);
    (And update it: image.setImageDrawable(getAnImageUrl());)

    Now the images are resized to a smaller size and are displayed aligning the left side of the screen.

    Would you please tell me the way out of this, so that the Image (-width) fills the screen? 🙂

    Thank you a lot in advance!
    Greetings from Germany,
    Max

    1. wow thanks! Hmm do you need android:scaleType=”fitXY” in your layout?

    2. Thx for your fast reply.
      Didn’t used scaleType but by adding it it doesn’t change anything.

      android:id=”@+id/loaderImageView”
      android:layout_width=”match_parent”
      android:layout_height=”wrap_content”
      android:scaleType=”fitXY”
      android:layout_centerHorizontal=”true”
      android:layout_centerVertical=”true”
      image=”http:/…”

    3. I found something:
      When I change minSdk from 4 to 3 the Image fills the screen, BUT it looks like the whole layout gets scaled (Action Bar is a lot to big, buttons seem to jump out of the screen, Option-items are to big for the screen and only readable halfly, …)

      Do you have any idea what the problem is?

      Cheers,
      Max

    4. Hi Max,

      I had the same problem. I ended up changing the ImageView parameters in code in the LoaderImageView class.
      Here is the compete function I changed.

      private void instantiate(final Context context, final String imageUrl) {
      mContext = context;

      mImage = new ImageView(mContext);
      mImage.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
      mImage.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
      mImage.setAdjustViewBounds(true);

      mSpinner = new ProgressBar(mContext);
      mSpinner.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));

      mSpinner.setIndeterminate(true);

      addView(mSpinner);
      addView(mImage);

      if(imageUrl != null){
      setImageDrawable(imageUrl);
      }
      }

Comments are closed.