Possible Duplicate:
Android - How do I do a lazy load of images in ListView
I am working on the listview with the custom adapter. I want to load the images and text view on it. The images are load from the internet urls. I just want to show the images which are visible list item to hte user. I refered the Shelves opensource project example from romainguy, but its to complicated to understand the code. For a beginner level, I just want to know how to handle the tag between the adapter and activity. From the commonsware example I can set the tag on adapter, but can't show the corresponding image at the idle state of the listview. Please help me with your ideas. Sample codes are more understandable.
Thanks.
EDIT:
The combination of Efficient and Slow Adapter in ApiDemos is more helpful to understand.
changes done on efficient adapter example like this:
public class List14 extends ListActivity implements ListView.OnScrollListener { // private TextView mStatus; private static boolean mBusy = false; static ViewHolder holder; public static class EfficientAdapter extends BaseAdapter { private LayoutInflater mInflater; private Bitmap mIcon1; private Bitmap mIcon2; public EfficientAdapter(Context context) { // Cache the LayoutInflate to avoid asking for a new one each time. mInflater = LayoutInflater.from(context); // Icons bound to the rows. mIcon1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon48x48_1); mIcon2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon48x48_2); } /** * The number of items in the list is determined by the number of * speeches in our array. * * @see android.widget.ListAdapter#getCount() */ public int getCount() { return DATA.length; } /** * Since the data comes from an array, just returning the index is * sufficent to get at the data. If we were using a more complex data * structure, we would return whatever object represents one row in the * list. * * @see android.widget.ListAdapter#getItem(int) */ public Object getItem(int position) { return position; } /** * Use the array index as a unique id. * * @see android.widget.ListAdapter#getItemId(int) */ public long getItemId(int position) { return position; } /** * Make a view to hold each row. * * @see android.widget.ListAdapter#getView(int, android.view.View, * android.view.ViewGroup) */ public View getView(int position, View convertView, ViewGroup parent) { // A ViewHolder keeps references to children views to avoid // unneccessary calls // to findViewById() on each row. // When convertView is not null, we can reuse it directly, there is // no need // to reinflate it. We only inflate a new View when the convertView // supplied // by ListView is null. if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item_icon_text, null); // Creates a ViewHolder and store references to the two children // views // we want to bind data to. holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); holder.icon = (ImageView) convertView.findViewById(R.id.icon); convertView.setTag(holder); } else { // Get the ViewHolder back to get fast access to the TextView // and the ImageView. holder = (ViewHolder) convertView.getTag(); } if (!mBusy) { holder.icon.setImageBitmap(mIcon1); // Null tag means the view has the correct data holder.icon.setTag(null); } else { holder.icon.setImageBitmap(mIcon2); // Non-null tag means the view still needs to load it's data holder.icon.setTag(this); } holder.text.setText(DATA[position]); // Bind the data efficiently with the holder. // holder.text.setText(DATA[position]); return convertView; } static class ViewHolder { TextView text; ImageView icon; } } private Bitmap mIcon1; private Bitmap mIcon2; @Override public void onCreate(Bundle savedInstanceState) { mIcon1 = BitmapFactory.decodeResource(this.getResources(), R.drawable.icon48x48_1); mIcon2 = BitmapFactory.decodeResource(this.getResources(), R.drawable.icon48x48_2); super.onCreate(savedInstanceState); setListAdapter(new EfficientAdapter(this)); getListView().setOnScrollListener(this); } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case OnScrollListener.SCROLL_STATE_IDLE: mBusy = false; int first = view.getFirstVisiblePosition(); int count = view.getChildCount(); for (int i = 0; i < count; i++) { holder.icon = (ImageView) view.getChildAt(i).findViewById( R.id.icon); if (holder.icon.getTag() != null) { holder.icon.setImageBitmap(mIcon1); holder.icon.setTag(null); } } // mStatus.setText("Idle"); break; case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: mBusy = true; // mStatus.setText("Touch scroll"); break; case OnScrollListener.SCROLL_STATE_FLING: mBusy = true; // mStatus.setText("Fling"); break; } } private static final String[] DATA = { "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu"}; }
It works fine now. But when the scrolling state its not reloading the image properly. some interval of items not shows the images2. that is the correct order of the images are loading. But not in all items of list. Mismatches happening between solid item intervals. how to correct it?
What is Image Lazy Loading? Lazy Loading Images is a set of techniques in web and application development that defer the loading of images on a page to a later point in time - when those images are actually needed, instead of loading them up front.
Instead of downloading the entire site in one go, which would take a long time for larger sites, your device downloads a series of placeholders that correspond to the actual information on the page. The browser caches all of the site's resources, but doesn't actually load them; it simply knows where each one goes.
For example, if a web page has an image that the user has to scroll down to see, you can display a placeholder and lazy load the full image only when the user arrives to its location.
Picasso is an open source image downloading and caching library, designed to automatically handle image loading on Android. You can find several use cases for loading an image from a URL instead of loading it from cache using Picasso.
I got it. This is the perfect code I want. Lazy loading works to custom adapter just visible list item's icons. Hope it helps to the beginners
public class List14 extends ListActivity implements ListView.OnScrollListener { // private TextView mStatus; private static boolean mBusy = false; static ViewHolder holder; public static class EfficientAdapter extends BaseAdapter { private LayoutInflater mInflater; private Bitmap mIcon1; private Bitmap mIcon2; private Context mContext; public EfficientAdapter(Context context) { // Cache the LayoutInflate to avoid asking for a new one each time. mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); mContext = context; // Icons bound to the rows. mIcon1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon48x48_1); mIcon2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon48x48_2); } /** * The number of items in the list is determined by the number of * speeches in our array. * * @see android.widget.ListAdapter#getCount() */ public int getCount() { return DATA.length; } /** * Since the data comes from an array, just returning the index is * sufficent to get at the data. If we were using a more complex data * structure, we would return whatever object represents one row in the * list. * * @see android.widget.ListAdapter#getItem(int) */ public Object getItem(int position) { return position; } /** * Use the array index as a unique id. * * @see android.widget.ListAdapter#getItemId(int) */ public long getItemId(int position) { return position; } /** * Make a view to hold each row. * * @see android.widget.ListAdapter#getView(int, android.view.View, * android.view.ViewGroup) */ public View getView(int position, View convertView, ViewGroup parent) { // A ViewHolder keeps references to children views to avoid // unneccessary calls // to findViewById() on each row. // When convertView is not null, we can reuse it directly, there is // no need // to reinflate it. We only inflate a new View when the convertView // supplied // by ListView is null. if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item_icon_text, parent, false); // Creates a ViewHolder and store references to the two children // views // we want to bind data to. holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); holder.icon = (ImageView) convertView.findViewById(R.id.icon); convertView.setTag(holder); } else { // Get the ViewHolder back to get fast access to the TextView // and the ImageView. holder = (ViewHolder) convertView.getTag(); } if (!mBusy) { holder.icon.setImageBitmap(mIcon1); // Null tag means the view has the correct data holder.icon.setTag(null); } else { holder.icon.setImageBitmap(mIcon2); // Non-null tag means the view still needs to load it's data holder.icon.setTag(this); } holder.text.setText(DATA[position]); // Bind the data efficiently with the holder. // holder.text.setText(DATA[position]); return convertView; } static class ViewHolder { TextView text; ImageView icon; } } private Bitmap mIcon1; private Bitmap mIcon2; @Override public void onCreate(Bundle savedInstanceState) { mIcon1 = BitmapFactory.decodeResource(this.getResources(), R.drawable.icon48x48_1); mIcon2 = BitmapFactory.decodeResource(this.getResources(), R.drawable.icon48x48_2); super.onCreate(savedInstanceState); setListAdapter(new EfficientAdapter(this)); getListView().setOnScrollListener(this); } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case OnScrollListener.SCROLL_STATE_IDLE: mBusy = false; int first = view.getFirstVisiblePosition(); int count = view.getChildCount(); for (int i = 0; i < count; i++) { holder.icon = (ImageView) view.getChildAt(i).findViewById( R.id.icon); if (holder.icon.getTag() != null) { holder.icon.setImageBitmap(IMAGE[first+i]);// this is the image url array. holder.icon.setTag(null); } } // mStatus.setText("Idle"); break; case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: mBusy = true; // mStatus.setText("Touch scroll"); break; case OnScrollListener.SCROLL_STATE_FLING: mBusy = true; // mStatus.setText("Fling"); break; } } private static final String[] DATA = { "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Yarra Valley Pyramid", "Yorkshire Blue", "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" }; }
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; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With