[android-developers] Re: Lazy Loading Images in a ListView

2010-01-14 Thread Sam Dutton
I haven't read through the whole thread, but FWIW there's a very good
introduction to list view 'lazy loading' techniques in Beginning
Android: http://www.apress.com/book/view/1430224193.

Look in the sample code under fancylists -- well documented in the
book.

Sam Dutton

On Dec 8 2009, 2:34 pm, Samuh samuh.va...@gmail.com wrote:
 I trying a hand at ListViews and my current experiment is aimed at
 displaying some data in a ListView. The data to be displayed in each
 row is simple: an image and some text. The images come from a remote
 server and the textual data is hardcoded.

 I have a class that downloads images using AsyncTask and caches the
 list of images fetched as SoftReferences in a LinkedHashMap. I am also
 passing a reference of the view to this class, so when the image
 download/cache read is complete the class will set appropriate Bitmap
 in the view.

 Following is my code:

 //class BasicAdapter extends ArrayAdapterRowData
 ...
 static class ViewHolder{
                 TextView text;
                 ImageView icon;
         }

         @Override
         public View getView(int position, View convertView, ViewGroup parent)
 {
                 ViewHolder holder;
                 if(convertView == null){
                         convertView = 
 mInflater.inflate(R.layout.listview_row,null);

                         holder = new ViewHolder();
                         holder.text = (TextView) 
 convertView.findViewById(R.id.row_txt);
                         holder.icon = (ImageView) 
 convertView.findViewById(R.id.row_img);

                         convertView.setTag(holder);
                 }
                 else{
                         holder = (ViewHolder) convertView.getTag();
                 }

                 holder.text.setText(mDataSet.get(position).getMText());

                 //set default icon
                 holder.icon.setImageResource(R.drawable.icon);

                 // set the actual image from the cache
                 String imageUrl = mDataSet.get(position).getMUrl();
                 WriteThroughCache.getImage(holder.icon, imageUrl);

                 return convertView;

         }

 // My helper class that manages cache and image Download

 public class WriteThroughCache{
         static LinkedHashMapString,SoftReferenceBitmap imageCache =
                 new LinkedHashMapString,SoftReferenceBitmap();

         public static void getImage(ImageView icon, String imageUrl){
                 if(imageCache.containsKey(imageUrl)){
                         Bitmap image = imageCache.get(imageUrl).get();
                         if(image != null){
                                 icon.setImageBitmap(image);
                                 return;
                         }
                 }

                 new LoadImageInBackground().execute(new 
 Object[]{icon,imageUrl});

         }

         static class LoadImageInBackground extends AsyncTaskObject, Void,
 Bitmap{
                 ImageView mIcon = null;
                 String mUrl = null;
                 @Override
                 protected Bitmap doInBackground(Object... params) {
                         mIcon = (ImageView)params[0];
                         mUrl = (String) params[1];
                         Bitmap image = BitmapFactory.decodeStream
 (GetMethodExecutor.getResponseStream(mUrl));
                         return image;
                 }
                 @Override
                 protected void onPostExecute(Bitmap result) {
                         super.onPostExecute(result);
                         mIcon.setImageBitmap(result);
                         imageCache.put(mUrl, new 
 SoftReferenceBitmap(result));
                 }

         }

 }

 Problem: The applications seems to work just fine. However, when I
 touch scroll the screen sometimes, the rows display the default image
 and Loading.. text. These are never refreshed.
 But if I fling the listview, everything works normal then.

 I am not sure of what I am missing here, please guide.

 Thanks.
-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Re: [android-developers] Re: Lazy Loading Images in a ListView

2010-01-14 Thread Mark Murphy
Sam Dutton wrote:
 I haven't read through the whole thread, but FWIW there's a very good
 introduction to list view 'lazy loading' techniques in Beginning
 Android: http://www.apress.com/book/view/1430224193.
 
 Look in the sample code under fancylists -- well documented in the
 book.

Hm...

I wrote that book, and I'm pretty sure I didn't cover the OP's question
there.

It's related to the Fancy ListViews chapter, but adding asynchronous
thumbnail loading is a whole 'nuther kettle of fish.

Fortunately for the OP, I have already filleted, breaded, and seasoned
that kettle of fish. Here's a reusable component that offers
asynchronous thumbnail downloading and application to a ListView:

http://github.com/commonsguy/cwac-thumbnail

-- 
Mark Murphy (a Commons Guy)
http://commonsware.com | http://twitter.com/commonsguy

_The Busy Coder's Guide to *Advanced* Android Development_
Version 1.3 Available!
-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

[android-developers] Re: Lazy Loading Images in a ListView

2009-12-10 Thread Samuh
skyhigh: thanks for your reply!

  I haven't tried to do a lazy background image load like you are doing,
  but have done some code that uses a holder/wrapper to store the layout
  findViewById which is a nice optimization so that it doesn't have to
  call inflate and findViewById again when each row is displayed.  My
  understanding is that when you do this it will allocate enough views
  for each of the rows that are visible on the screen, and then as a row
  scrolls off the screen, it will reuse the already initialized view for
  a new row that is scrolling onto the screen.

As you can see, I have used a view holder pattern too; as was
explained in the Google I/O conference by Romain.

  In looking at your code I think you may have a problem because of the
  way that the views for each row get reused when they disappear from
  sight.  The same view that was used for a row that just disappeared
  can be reused for a new row that is appearing as the list scrolls.
  Your getView routine is handling this by detecting that the
  convertView is non-null when it has already been filled in when this
  view was used by a previous row.  However you don't have any mechanism
  to detect when a view has been reused and is now displaying a
  different row and the URL has changed to a different URL while the
  background AsyncTask was retrieving the image.  By the time the
  AsyncTask finishes retrieving the image, the view that it saved the
  reference to, in order to set the image, could have been reused to
  display a different row in the list.  If you can add code to check if
  it is still the expected URL prior to setting the image I think it
  might solve your problem.  If the URL has changed then skip setting
  the lazy image on that view, and just add the image to your cache.

Thanks for pointing this out; I was able to recreate the situation
that you've described by inserting a delay in the doInBackground()
method of AsyncTask. Result: When I scrolled a few dozen rows and
stopped, the icons in the new Frame displayed the older icons first
and then the list was refreshed.

I could probably work around this by:
1. Tag the view with some unique Id and when setting the image in the
AsyncTask, find the View by Tag, and then proceed with setImage(..).
2. I think, I could optimize the implementation further by not loading
the image when the list is scrolling; similar to what SlowAdapter does.

-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en


[android-developers] Re: Lazy Loading Images in a ListView

2009-12-10 Thread Samuh
On Dec 9, 1:07 pm, Romain Guy romain...@android.com wrote:
 You can check out the example I wrote at code.google.com/p/shelves. It
 does lazy loading of images from the sdcard.

Thanks Romain for your time; I will surely check the implementation
out.

Cheers!

-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en


[android-developers] Re: Lazy Loading Images in a ListView

2009-12-10 Thread Lee
I have also recently been implementing adapter-based image loading
into a list.

I found that when I used SoftReferences, they started getting cleared
very quickly indeed, after my cache of thumbnails was up to maybe 20
or so.

When I use normal references, the cache grows happily to a couple of
hundred thumbnails without OOMs being thrown.

Being a java newbie, I don't pretend to understand this, but I went
with normal references and careful OOM catching.

BTW, tip maybe, I used onScroll and onScrollStateChanged from the list
to initiate cache fills (rather than filling as a result of calls from
getView()) because otherwise I found scrolling getting sluggish as the
image cache got busy
filling the cache while scrolling was still ongoing.

Lee

On Dec 10, 2:21 pm, Samuh samuh.va...@gmail.com wrote:
 On Dec 9, 1:07 pm, Romain Guy romain...@android.com wrote:

  You can check out the example I wrote at code.google.com/p/shelves. It
  does lazy loading of images from the sdcard.

 Thanks Romain for your time; I will surely check the implementation
 out.

 Cheers!

-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en


[android-developers] Re: Lazy Loading Images in a ListView

2009-12-10 Thread Lee
Oh nevermind, I saw you realised the scroll problem already :-)

Lee

-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en


[android-developers] Re: Lazy Loading Images in a ListView

2009-12-10 Thread Samuh
 I found that when I used SoftReferences, they started getting cleared
 very quickly indeed, after my cache of thumbnails was up to maybe 20
 or so.

 When I use normal references, the cache grows happily to a couple of
 hundred thumbnails without OOMs being thrown.

 Being a java newbie, I don't pretend to understand this, but I went
 with normal references and careful OOM catching.

One of the possible reasons, I can think of, why Romain chose to
persist the fetched images on SDcard(in his Shelves app) is because
when caching is done on heap, the objects keep getting garbage
collected so you have to repeat the time consuming fetches.

-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en


Re: [android-developers] Re: Lazy Loading Images in a ListView

2009-12-09 Thread Romain Guy
You can check out the example I wrote at code.google.com/p/shelves. It
does lazy loading of images from the sdcard.

On Tue, Dec 8, 2009 at 11:45 PM, skyhigh skyhigh1...@gmail.com wrote:
 I haven't tried to do a lazy background image load like you are doing,
 but have done some code that uses a holder/wrapper to store the layout
 findViewById which is a nice optimization so that it doesn't have to
 call inflate and findViewById again when each row is displayed.  My
 understanding is that when you do this it will allocate enough views
 for each of the rows that are visible on the screen, and then as a row
 scrolls off the screen, it will reuse the already initialized view for
 a new row that is scrolling onto the screen.

 In looking at your code I think you may have a problem because of the
 way that the views for each row get reused when they disappear from
 sight.  The same view that was used for a row that just disappeared
 can be reused for a new row that is appearing as the list scrolls.
 Your getView routine is handling this by detecting that the
 convertView is non-null when it has already been filled in when this
 view was used by a previous row.  However you don't have any mechanism
 to detect when a view has been reused and is now displaying a
 different row and the URL has changed to a different URL while the
 background AsyncTask was retrieving the image.  By the time the
 AsyncTask finishes retrieving the image, the view that it saved the
 reference to, in order to set the image, could have been reused to
 display a different row in the list.  If you can add code to check if
 it is still the expected URL prior to setting the image I think it
 might solve your problem.  If the URL has changed then skip setting
 the lazy image on that view, and just add the image to your cache.

 --
 You received this message because you are subscribed to the Google
 Groups Android Developers group.
 To post to this group, send email to android-developers@googlegroups.com
 To unsubscribe from this group, send email to
 android-developers+unsubscr...@googlegroups.com
 For more options, visit this group at
 http://groups.google.com/group/android-developers?hl=en




-- 
Romain Guy
Android framework engineer
romain...@android.com

Note: please don't send private questions to me, as I don't have time
to provide private support.  All such questions should be posted on
public forums, where I and others can see and answer them

-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en


[android-developers] Re: Lazy Loading Images in a ListView

2009-12-08 Thread skyhigh
I haven't tried to do a lazy background image load like you are doing,
but have done some code that uses a holder/wrapper to store the layout
findViewById which is a nice optimization so that it doesn't have to
call inflate and findViewById again when each row is displayed.  My
understanding is that when you do this it will allocate enough views
for each of the rows that are visible on the screen, and then as a row
scrolls off the screen, it will reuse the already initialized view for
a new row that is scrolling onto the screen.

In looking at your code I think you may have a problem because of the
way that the views for each row get reused when they disappear from
sight.  The same view that was used for a row that just disappeared
can be reused for a new row that is appearing as the list scrolls.
Your getView routine is handling this by detecting that the
convertView is non-null when it has already been filled in when this
view was used by a previous row.  However you don't have any mechanism
to detect when a view has been reused and is now displaying a
different row and the URL has changed to a different URL while the
background AsyncTask was retrieving the image.  By the time the
AsyncTask finishes retrieving the image, the view that it saved the
reference to, in order to set the image, could have been reused to
display a different row in the list.  If you can add code to check if
it is still the expected URL prior to setting the image I think it
might solve your problem.  If the URL has changed then skip setting
the lazy image on that view, and just add the image to your cache.

-- 
You received this message because you are subscribed to the Google
Groups Android Developers group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en