Eduardo
Eduardo

Reputation: 7131

Android: How to set the image in this ListView?

How do i set the images in this layout?

List Entry

The ListView will contain 3 of the above entries, each image will be downloaded in an AsyncTask (See Below) and the text will be filled in by a String array of preset Strings eg

String[] values = {"string one", "string two", "String three"};

I want to be able to first set the String content values of all 3 entries using the adapter below, then have AsyncTasks running in the background downloading and setting the icons for each entry.

The Strings are more important than the icons so i dont want the user to have to wait for each icon to download before the string is set.

I have a ListView Layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:paddingBottom="@dimen/small_8dp"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/small_8dp" >
    
    <ImageView
        android:id="@+id/logo"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:contentDescription="@string/loading"
        android:scaleType="fitXY"
        android:src="@drawable/image" >

    </ImageView>
 
    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="@dimen/small_8dp"
        android:text="@string/loading"
        android:textSize="@dimen/medium_15dp" >

    </TextView>

</LinearLayout>

Which is in a Layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/color_white"
    android:orientation="vertical" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:entries="@array/list_headlines">
    </ListView>

</LinearLayout>

Ive been working with this custom adapter:

private class ArticleAdapter extends ArrayAdapter<String>{
        private final Context context;
        private final String[] values;
    
        public ArticleAdapter(Context context, String[] values) {
            super(context, R.layout.list_entry, values);
            this.context=context;
            this.values=values;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View rowView = inflater.inflate(R.layout.list_entry,parent,false);
            TextView tv = (TextView) rowView.findViewById(R.id.label);
            
            tv.setText(values[position]);
            
            return rowView;
        }
    }

LoadThumbnail AsyncTask:

protected class LoadThumbnail extends AsyncTask<String, Void, String> {
        private String url;
        private boolean loaded; //if loaded set the bitmap image to whats downloaded
        private Bitmap icon;
        private int iconIndex;
        
        public LoadThumbnail(int iconIndex, String url){
            loaded = false; 
            this.url = url; //url of the icon to download
            this.iconIndex=iconIndex; //Which icon in the listview were downloading
        }
        
        @Override
        protected String doInBackground(String... params) {
            Download download = new Download(url); //My Download Class
            try {
                icon = download.downloadImage(); //Returns A Bitmap image
                loaded=true; //If no errors caught
            } catch (Exceptions e) {
                //Various Exception Handling Here
            } 
            return null;
        }

        
        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
        }
        
        
    }

Can you tell me which functions i have to adapt to achieve this? Thanks!

Upvotes: 1

Views: 3982

Answers (4)

Nishant Bhakta
Nishant Bhakta

Reputation: 2987

This answer is late but may be helpful for others.

In your code, if your AsyncTask<> is located as separate class then you can simply instantiate it with giving it ImageView in you adapter class, like

new LoadThumbnail(imageView).execute(url);

then in your LoadThumbnail class simply set bitmap in imageView.

i am writing this roughly, please make it as you want;

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View rowView = inflater.inflate(R.layout.list_entry,parent,false);
    TextView tv = (TextView) rowView.findViewById(R.id.label);
    ImageView imageView = (ImageView)rowView.findViewById(R.id.logo);
    tv.setText(values[position]);

    //Instantiate LoadThumbnail giving it imageView to set bitmap in.
    new LoadThumbnail(imageView).execute(url);

    return rowView;
}

Now in LoadThumbnail simply set bitmap in onPostExecute() method.

protected class LoadThumbnail extends AsyncTask<String, Void, Bitmap> {
        private ImageView imageView;

        public LoadThumbnail(ImageView imageView){
            this.imageView = imageView;
        }

        @Override
        protected String doInBackground(String... url) {
            Bitmap icon = null;
            Download download = new Download(url[0]); //My Download Class
            try {
                icon = download.downloadImage(); //Returns A Bitmap image
                loaded=true; //If no errors caught
            } catch (Exceptions e) {
                //Various Exception Handling Here
            } 
            return icon;
        }

        @Override
        protected void onPostExecute(Bitmap icon) {
            super.onPostExecute(icon);

            //here set bitmap in imageView;
            if(icon != null){
                imageView.setImageBitmap(icon);
            }
        }
    }

Upvotes: 0

krishan711
krishan711

Reputation: 873

In getView(), you should start a new task to download the image for that list item. Pass a reference of the ImageView you want populated to the AsyncTask, and then set the source for that ImageView to the downloaded bitmap (icon) in onPostExecute().

This does get a little complicated as you will have to deal with Views being recycled by the ListView (by cancelling the tasks to download the images). I like to use this library for all my downloading and caching of images: http://square.github.io/picasso/

Upvotes: 1

Rarw
Rarw

Reputation: 7663

The easiest way to do this is to download your images to some Collection and then for the list to update when the downloads are complete by calling notifyDatasetChanged on the Adapter.

Before we get started - I don't know where your AsyncTask is located. If its a separate class you need to use an Interface to set up a call back to the place you're starting it from. If its an inner class within your Adaper you can do this localy.

What you're going to do is set up your Adapter to (1) check if the image you want is available (2) if not download the image and add it to a collection (3) when the image is downloaded refresh the list (or when all images are downloaded depening how long this list is).

This all happens when you set the content for a specific list item

if(logo != null){
    if(imageCollection.contains(key){
        logo.setImageBitmap(imageCollection.get(key));
    } else {
        thumbnailDownloader.execute;
    }
} 

If you're AsyncTask is an inner class witin your adapater then within onPostExecute you will (1) add the image to this collection (2) update the ListView using notifyDatasetChanged. If your AsyncTask is it's own class you would add the image to the collction in your callback and update the list from there as well.

For the collection it's easier if you use the LruCache built into android but you could use a HashMap or something else. Just depending on your implementation.

Upvotes: 2

abbath
abbath

Reputation: 2482

Firstly I'd suggest to give a reference of the ArticleAdapter to the AsyncTask.
Then create a Bitmap array or map in ArticleAdapter, and create an add(Bitmap bmp) method for the ArticleAdapter, which puts a bitmap object into the array/map.
Because you have reference in the AsyncTask, you can call the add() method in the onPostExecute() method, with the downloaded icon Bitmap.
Then you can call the ArticleAdapter's notifyDataSetChanged(), so it can refresh its views.

Of course your Adapter's getView should check its bitmap array/map, if the bitmap for the given key is already downloaded. if it's reference is null, then put a placeholder, if it is downloaded already, place the bitmap which is put in the array/map by the asynctask.

Upvotes: 1

Related Questions