How to Lazy Load Images in Listview in Android

How to lazy load images in ListView in Android

Here's what I created to hold the images that my app is currently displaying. Please note that the "Log" object in use here is my custom wrapper around the final Log class inside Android.

package com.wilson.android.library;

/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;

public class DrawableManager {
private final Map<String, Drawable> drawableMap;

public DrawableManager() {
drawableMap = new HashMap<String, Drawable>();
}

public Drawable fetchDrawable(String urlString) {
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}

Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");


if (drawable != null) {
drawableMap.put(urlString, drawable);
Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
} else {
Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
}

return drawable;
} catch (MalformedURLException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
if (drawableMap.containsKey(urlString)) {
imageView.setImageDrawable(drawableMap.get(urlString));
}

final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};

Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}

private InputStream fetch(String urlString) throws MalformedURLException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
return response.getEntity().getContent();
}
}

Lazy Load images on Listview in android(Beginner Level)?

Praveen -

As you already found my blog post on this, I just wanted to push it back to Stackoverflow so that others can use it.

Here's the basic discussion:
http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/

And there's a class I documented later that uses a thread and a callback to load the images:

http://ballardhack.wordpress.com/2010/04/10/loading-images-over-http-on-a-separate-thread-on-android/

Update: To address your specific exception, I think that view returned in the list from getChildAt is not an ImageView -- it's whatever layout view you are using to hold the image and text.

Update to include relevant code: (Per @george-stocker's recommendation)

Here is the adapter I was using:

public class MediaItemAdapter extends ArrayAdapter<MediaItem> {
private final static String TAG = "MediaItemAdapter";
private int resourceId = 0;
private LayoutInflater inflater;
private Context context;

private ImageThreadLoader imageLoader = new ImageThreadLoader();

public MediaItemAdapter(Context context, int resourceId, List<MediaItem> mediaItems) {
super(context, 0, mediaItems);
this.resourceId = resourceId;
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.context = context;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

View view;
TextView textTitle;
TextView textTimer;
final ImageView image;

view = inflater.inflate(resourceId, parent, false);

try {
textTitle = (TextView)view.findViewById(R.id.text);
image = (ImageView)view.findViewById(R.id.icon);
} catch( ClassCastException e ) {
Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e);
throw e;
}

MediaItem item = getItem(position);
Bitmap cachedImage = null;
try {
cachedImage = imageLoader.loadImage(item.thumbnail, new ImageLoadedListener() {
public void imageLoaded(Bitmap imageBitmap) {
image.setImageBitmap(imageBitmap);
notifyDataSetChanged(); }
});
} catch (MalformedURLException e) {
Log.e(TAG, "Bad remote image URL: " + item.thumbnail, e);
}

textTitle.setText(item.name);

if( cachedImage != null ) {
image.setImageBitmap(cachedImage);
}

return view;
}
}

Lazy loading of images in ListView

By far the easiest libaray I have used which caches images and works really well is AQuery Image Loading Lib. I use it all the time for image loading and caching, really simple.

Android - Issue with lazy loading images into a ListView

In the linked solution, fetchDrawableOnThread() should only be called if the view does not already have the correct drawable.

A view does not have a drawable if getDrawable() returns null.

If you are reusing slots, you views you need to go further and manage the state. If your views have a member variable storing the URL, and a boolean to say whether it is loaded, it'd be easy to know whether to call fetchDrawableOnThread() or not, for example.

I'd speculate that the drawable's toString() detailed the path from which the image was loaded. (If it doesn't, you could subclass the drawable returned to make it so). In this case, you could avoid the boolean outlined above and just do a comparison to determine if its the right drawable or whether to fetch a replacement.

Additionally, your getView() on a visible row should ensure that those that no longer visible get unloaded, to prevent memory exhaustion. A finesse would be to move the no longer visible images to soft references (so they are unloaded when memory is needed) as another poster on the original thread noted.

Lazy load of images on ListView with AsyncTask launched on the getView (adapter)

see git lazylist for the code....

Using the code to do what you ask...

OnClickInList OR in the Fragment that wraps the view with bitmaps u need...

  imgLoadr.DisplayImage(mMap.get("pic"), imgview);

above calls into branching code depends on ( bitmap in memCache Y/N, bitmap in local Files Y/N, bitmap avail across the Web via URL ).

You dont have to worry about the implementation for the branches above .Lib takes care of it for u.

DisplayImage(){}...
if(bitmap!=null){
imageView.setImageBitmap(bitmap);
}
else
{
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);

Set your HTTP HEADERS in the runnable...

 public Bitmap getBitmap(String url)
{

File f=fileCache.getFile(url);
//from SD cache
Bitmap b = decodeFile(f);
if(b!=null)
return b;

//from web
try {
Bitmap bitmap=null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex){

get the project lazylist

In you

Android listview lazy loading

You can achieve this by using endless Adapter implementation. This exactly does what you want. You can also restrict the number of rows to be refreshed per scroll. Here is a link to it.,

Android: Implementing progressbar and "loading..." for Endless List like Android Market

https://github.com/commonsguy/cwac-endless

To use it, you extend EndlessAdapter to provide details about how to handle the endlessness. Specifically, you need to be able to provide a row View, independent from any of the rows in your actual adapter, that will serve as a placeholder while you, in another method, load in the actual data to your main adapter. Then, with a little help from you, it seamlessly transitions in the new data.



Related Topics



Leave a reply



Submit