Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ListView with ArrayAdapter and ViewHolder adding icons to the wrong item

I have a dynamic ListView which uses an ArrayAdapter. When a name is selected from a spinner, the name together with an icon showing whether they are male or female gets added to the ListView.

Mostly everything is good (the name gets added to the list correctly, together with an icon). But the icon showing the sex gets added to the wrong item in the ListView. The name gets added to the bottom of the list, but the icon gets placed at the name at the top of the list. I don't know if it's the way I'm using ViewHolder but there is zero documentation on it in the Android website.

// Listview inflater
inflater = (LayoutInflater) (this).getSystemService(LAYOUT_INFLATER_SERVICE);

// List Array.
mAdapter = new ArrayAdapter<String>(this, R.layout.player_simple_list, 
                                                 R.id.label, mStrings) {

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

        Log.i("ANDY","View getView Called");
        // A ViewHolder keeps references to children views to 
        // avoid unneccessary calls to findViewById() on each row.
        ViewHolder holder;

        if (null == convertView) {
            Log.i("ANDY","Position not previously used, so inflating");
            convertView = inflater.inflate(R.layout.player_simple_list, 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.label);
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);
            if (sexmale == true) {
                holder.icon.setImageBitmap(maleicon);
            }
            else {
                holder.icon.setImageBitmap(femaleicon);
            }
            convertView.setTag(holder);
        } else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
            holder = (ViewHolder) convertView.getTag();

        }
        // Bind the data efficiently with the holder.
        holder.text.setText(getItem(position));
        // Change icon depending is the sexmale variable is true or false.
        Log.i("ANDY","getCount = "+mAdapter.getCount());
        return convertView;
    }
};
setListAdapter(mAdapter);
like image 885
andy_spoo Avatar asked Jun 26 '10 08:06

andy_spoo


3 Answers

You have to set the icons after if-else-if for creating or binding a holder. Otherwise, the icons would be rightly displayed only in first few items in the list i.e until the ListView is not filled.

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

    Log.i("ANDY","View getView Called");
    // A ViewHolder keeps references to children views
    // to avoid unneccessary calls to findViewById() on each row.
    ViewHolder holder;

        if (null == convertView) {
            Log.i("ANDY","Position not previously used, so inflating");
            convertView = inflater.inflate(R.layout.player_simple_list, 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.label);
            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();

        }
        // Bind the data efficiently with the holder.
        holder.text.setText(getItem(position));

        // Change icon depending is the sexmale variable is true or false.
        if (sexmale == true) {
            holder.icon.setImageBitmap(maleicon);
        }
        else {
            holder.icon.setImageBitmap(femaleicon);
        }
        Log.i("ANDY","getCount = "+mAdapter.getCount());
        return convertView;
}
like image 150
Primal Pappachan Avatar answered Nov 07 '22 23:11

Primal Pappachan


You have to move from the if a few lines of data after the comment, as in this question is explained

// Bind the data efficiently with the holder.

so it will look like this

if (null == convertView) {
    Log.i("ANDY","Position not previously used, so inflating");
    convertView = inflater.inflate(R.layout.player_simple_list, null);
    // Creates a ViewHolder and store references to the two children views
    // we want to bind data to.
    holder = new ViewHolder();
    convertView.setTag(holder);
} else {
    // Get the ViewHolder back to get fast access to the TextView
    // and the ImageView.
    holder = (ViewHolder) convertView.getTag();
}

// Bind the data efficiently with the holder.
holder.text = (TextView) convertView.findViewById(R.id.label);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
if (sexmale == true) {
    holder.icon.setImageBitmap(maleicon);
}
else {
    holder.icon.setImageBitmap(femaleicon);
}
holder.text.setText(getItem(position));
like image 45
Pentium10 Avatar answered Nov 07 '22 23:11

Pentium10


Update: ViewHolder is only meant to hold references to the component views inside the item layout. This helps to avoid the overhead of calling findViewById for rendering each component inside complex item layouts with multiple components(Like the TextView, and ImageView in this case).

I fixed it by using a routine (called getSex) to retrieve the sex data and setting all the view data including icons outside the if-else blocks.

The working code now looks like this:

if (null == convertView) {
    Log.i("ANDY","Position not previously used, so inflating");
    convertView = inflater.inflate(R.layout.player_simple_list, 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.label);
    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();
}

// Bind the data efficiently with the holder.
holder.text.setText(getItem(position));
// Change icon depending is the sexmale variable is true or false.
if (getSex (getItem(position)) == true)  {
    holder.icon.setImageBitmap(maleicon);
}
else {
    holder.icon.setImageBitmap(femaleicon);
}
return convertView;
like image 1
andy_spoo Avatar answered Nov 07 '22 23:11

andy_spoo