0

I have a problem with the Android UI that I can't seem to solve. My main activity (a ListActivity) displays a list of items. When the user clicks one, it causes the underlying data to be updated (queried from a web service) through an AsyncTask. When the request terminates successfully, I call notifyDataSetChanged() to have the new information displayed. It is my understanding that the getView method from my custom BaseAdapter will be called to perform the rendering :

private List<InstalledApp> data;

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    if (convertView == null) {
        convertView = LayoutInflater.from(ctx).inflate(R.layout.list_item, parent, false);
    }
    InstalledApp app = data.get(position);
    String latest_version = app.getLatestVersion();
    TextView version = (TextView) convertView.findViewById(R.id.version);

    if (latest_version != null)
    {
        if (app.getVersion().equals(latest_version))
        {
            version.setText(app.getVersion());
            version.setTextColor(Color.GREEN);
        }
        else
        {
            version.setText(app.getVersion() + " (Current: " + latest_version + ")");
            version.setTextColor(Color.RED);
        }
    }
    else {
        version.setText(app.getVersion());
    }

    return convertView;
}

The problem is that when an item is updated and this gets called, two items have their colors changed. But the textual content remain correct. The second one is usually not visible immediately, and I have to scroll down the list to see it. Do you have any idea what could be causing this?

executifs
  • 1,138
  • 1
  • 9
  • 23

3 Answers3

1

This problem happens, because convertView is being recycled. To solve your issue, just make sure, that when you set some properties for convertView inside IF statement, you need to reset those properties inside ELSE statement, to make sure that when view is reused, and particular condition no longer applies, your properties are back to default.

In your particular case:

  • for first item on list, these conditions may be true: (latest_version != null) AND (app.getVersion().equals(latest_version)), so you set your textColor as GREEN

  • then you scroll down, and this view is reused for another list item, since is no longer visible (this is how listView is designed for performance reasons). Now, different conditions may be true for this new list item, in particular this: (latest_version == null) -> which causes this line to be called version.setText(app.getVersion());, so your new list item has correct text, but you didnt set textColor for this scenario, so textColor stays the same (that was set before view was reused).

So to solve your problem, see code below:

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    if (convertView == null) {
        convertView = LayoutInflater.from(ctx).inflate(R.layout.list_item, parent, false);
    }
    InstalledApp app = data.get(position);
    String latest_version = app.getLatestVersion();
    TextView version = (TextView) convertView.findViewById(R.id.version);

    if (latest_version != null)
    {
        if (app.getVersion().equals(latest_version))
        {
            version.setText(app.getVersion());
            version.setTextColor(Color.GREEN);
        }
        else
        {
            version.setText(app.getVersion() + " (Current: " + latest_version + ")");
            version.setTextColor(Color.RED);
        }
    }
    else {
        version.setText(app.getVersion());
        version.setTextColor(Color.GREEN); // YOU NEED TO ALSO SET CORRECT COLOR HERE
    }

    return convertView;
}
hendrix
  • 3,364
  • 8
  • 31
  • 46
1

How ListView's recycling mechanism works

You are facing the problem because listview recycles views.

You can try the below to overcome it

public class InstalledApp
{
   ...// rest of the code
   ...// setter an getter for version
   int color;
   public void setColor(int color)
   {
       this. color = color;
   }  
   public int getColor()
   {
        return color;
   }
}

While populating List<InstalledApp> data;. This list can be populated in a loop. THe below is just an example.

 InstalledApp app = new Installedapp();
 if(version.equals(latest_version); // you condition to check latest version
 { 
       app.setColor(Color.GREEN);
 } 
 else
 {
       app.setColor(Color.RED);
 } 
 ...// setter for version here
 data.add(app); 

Pass the list populated to the constructor of adapter class

Then in getView

InstalledApp app = data.get(position);
version.setText(app.getVersion() + " (Current: " + latest_version + ")");
version.setTextColor(app.getColor());
Community
  • 1
  • 1
Raghunandan
  • 132,755
  • 26
  • 225
  • 256
0

Try this

private List<InstalledApp> data;
 int[] colors=new int[data.size()];
 @Override
 public View getView(int position, View convertView, ViewGroup parent)
  {
    if (convertView == null) {
    convertView = LayoutInflater.from(ctx).inflate(R.layout.list_item, parent, false);
  }
 InstalledApp app = data.get(position);
 String latest_version = app.getLatestVersion();
 TextView version = (TextView) convertView.findViewById(R.id.version);

 if (latest_version != null)
 {
    if (app.getVersion().equals(latest_version))
    {
        version.setText(app.getVersion());
        colors[position]=Color.GREEN;
        version.setTextColor(colors[position]);
    }
    else
    {
        version.setText(app.getVersion() + " (Current: " + latest_version + ")");
        colors[position]=Color.RED;
        version.setTextColor(colors[position]);
    }
}
else {
       version.setText(app.getVersion());
}

return convertView;

}

vaibhav kumar
  • 414
  • 2
  • 11
  • Thanks for taking the time to reply. I prefer the other answers, because keeping track of all the colors seems unnecessary to me. – executifs Jan 14 '14 at 11:31