Android ListView with images using Droid-Fu WebImageView and a custom list adapter

Droid-Fu library has a nice little widget called WebImageView which allows the display of an image downloaded from the Web, along with a nice little loading animation display. The widget cannot be simpler to use:

WebImageView imageView = ...
imageView.setImageUrl("http://...");
imageView.loadImage();

That’s it! The widget will automatically handle the display of an indeterminate progress animation, and display the image as soon as it is downloaded.Moreover: downloaded images are cached. Awesome!

There is little to no mention about it, but WebImageViews can also be used in ListViews in order to display multiple dynamically-loaded images coming from the Web. This article highlights the steps required to do so with an example application that loads a randomly chosen Picasa album feed and displays the thumbnail pictures of every image in the album.

Application screenshot

First, we need to create a layout for the list items:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="horizontal"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:gravity="center"
              android:background="#FFF"
              android:padding="5dip">

    <com.github.droidfu.widgets.WebImageView android:id="@+id/webimage"
                                             android:layout_width="75dip"
                                             android:layout_height="75dip"
                                             android:background="#CCC"/>
    
    <TextView android:id="@+id/title"
              android:textStyle="bold"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:layout_gravity="left|center"/>

</LinearLayout>

Nothing fancy here, a WebImageView will be displayed on the left side of every item in the list.Next step would be to create a ListView in your main application layout. I’ll omit an example of this for the sake of brevity.

Let’s create an Adapter for our custom list and list items:

public class PhotoEntriesListAdapter extends BaseAdapter {

   List photos = Collections.emptyList();
   Context ctx;

   public PhotoEntriesListAdapter(Context ctx) {
       this.ctx = ctx;
   }

   public void setPhotos(List photos) {
       this.photos = photos;
       if (this.photos == null) {
           this.photos = Collections.emptyList();
       }
   }

   public int getCount() {
       return photos.size();
   }

   public Object getItem(int i) {
       return photos.get(i);
   }

   public long getItemId(int i) {
       Message item = (Message) getItem(i);
       return item.getLink().hashCode();
   }

   public View getView(int position, View convertedView, ViewGroup viewGroup) {

       Message entry = (Message) getItem(position);

       if (convertedView == null) {
           convertedView = LayoutInflater.from(ctx).inflate(R.layout.list_item, null);
       }

       WebImageView imageView = (WebImageView) convertedView.findViewById(R.id.webimage);
       TextView txtTitle = (TextView) convertedView.findViewById(R.id.title);

       txtTitle.setText(entry.getTitle());

       String imageUrl = entry.getMediaContentUrl();

       // calling reset is important in order to make sure an old image from the recycled
       // view is not displayed while loading
       imageView.reset();

       if (imageUrl != null) {
           imageView.setImageUrl(entry.getMediaContentUrl());
           imageView.loadImage();
       }

       return convertedView;
   }
}

This code should be pretty descriptive.One word about the reset() method though. As highlighted by the comment, it is important to call reset() on the WebImageView in the context of a list. Since views get recycled (re-used) by Android for performance, there is a good chance that WebImageView being re-used already displays an image. Calling reset() in the getView() method allows to old image to be discarded so that we don’t see that old image while the new image gets downloaded in a background thread.

Next, plug it all together in your main Activity code:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    lstImages = (ListView) findViewById(R.id.mainList);
    txtFeedName = (TextView) findViewById(R.id.feedName);

    listAdapter = new PhotoEntriesListAdapter(this);
    lstImages.setAdapter(listAdapter);

    loadPhotos();
}

private void loadPhotos() {
    new LoadPhotosTask().execute(FEED_URL);
}

private class LoadPhotosTask extends AsyncTask> {
    private String feedUrl;
    private ProgressDialog progressDialog;

    protected void onPreExecute() {
        progressDialog = ProgressDialog.show(MainActivity.this,
                "Loading feed...", "Please wait...", true);
    }

    protected List doInBackground(String... feedUrls) {
        this.feedUrl = feedUrls[0];

        try{
            FeedParser parser = FeedParserFactory.getParser(ParserType.SAX, feedUrl);

            return parser.parse();

        } catch (Throwable t){
            Log.e(getClass().getSimpleName() , t.getMessage(), t);
        }
        return null;
    }

    protected void onPostExecute(List photoEntries) {

        try {

            txtFeedName.setText(feedUrl);

            listAdapter.setPhotos(photoEntries);
            listAdapter.notifyDataSetChanged();
        } finally {

            progressDialog.dismiss();
        }
    }
}

This snippet also highlights best practices for handling long-running tasks in Android, along with the display of a nice progress dialog while data is loading.

That’s it!

It is worth mentioning that, as of now, the current release of Droid-Fu’s WebImageView acts weirdly when used in a ListView (WebImageViews can flicker or display the wrong image). I have made a fix to the library which I plan to submit as a pull request on Droid-Fu project very soon.

UPDATE: Just submitted the pull request.

UPDATE 2: The pull request has been pulled in the main branch, so the fix in the latest release of Droid-fu!

Tags: android
comments powered by Disqus