Search code examples
androidlistviewandroid-arrayadapterandroid-listfragment

Disappearing images ListView


I have a problem with list view.

In my app there's a ListFragment which holds a list of notifications which are basically a text and an image.

They also might be disabled, when clicked. On click I set a field 'active' of struct Notification to false and call NotifyDataSetChanged method. But when i scroll it up and down, some images disappear and might reappear again. But not when I don't scroll it.

All images are in resources and i never set visibility of list items.

Here's some of my code:

Custom ArrayAdapter

public class NotificationsArrayAdapter extends ArrayAdapter<Notification> {
    private final Context context;
    private final String[] message_strings;
    private final Drawable[] images;
    private final LayoutInflater layoutInflater;
    private ArrayList<Notification> items;

    public NotificationsArrayAdapter (Context context, Resources resources, ArrayList<Notification> items) {
    super(context, R.layout.customer_notifications_list_item, items);
    this.context = context;
    this.items = items;
    this.message_strings = new String[] {"message1", "message2", "message3", "message4"};

    this.images = new Drawable[] {ContextCompat.getDrawable(context, R.drawable.notifications_blue_circle),
                ContextCompat.getDrawable(context, R.drawable.notifications_red_circle),
                ContextCompat.getDrawable(context, R.drawable.notifications_grey_circle),
                ContextCompat.getDrawable(context, R.drawable.notifications_green_circle)
        };

        this.layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

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

        if (v == null) {
            v = layoutInflater.inflate(R.layout.customer_notifications_list_item, parent, false);
        }

        Notification item = getItem(position);

        if (item != null) {
            TextView textView = (TextView) v.findViewById(R.id.notifications_list_item_text);
            ImageView imageView = (ImageView) v.findViewById(R.id.notifications_list_item_image);

            if (textView != null) {
                if (item.getActive()) {
                    textView.setTextColor(Color.parseColor("#FFFFFF"));
                }
                else {
                    textView.setTextColor(Color.parseColor("#BBBBBB"));
                }
                textView.setText(message_strings[item.getType()]);
            }

            if (imageView != null) {
                if (item.getActive()) {
                    imageView.setImageDrawable(images[item.getType()]);
                }
                else {
                    imageView.setImageAlpha(0);
                }
            }
        }

    return v;
    }
}

Fragment:

public class CustomerNotificationsFragment extends ListFragment {

    public CustomerNotificationsFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_customer_notifications, container, false);

        ArrayList<Notification> notifications = new ArrayList<Notification>();
        for (int i = 0; i < 20; ++i) {
            notifications.add(new Notification(1, "blabal"));
        }

        NotificationsArrayAdapter notificationsArrayAdapter = new NotificationsArrayAdapter(getActivity(), getResources(), notifications);
        setListAdapter(notificationsArrayAdapter);

        return view;
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        Notification item = (Notification) this.getListAdapter().getItem(position);
        item.setActive(false);

        ((ArrayAdapter) getListAdapter()).notifyDataSetChanged();
    }
}

Solution

  • The views in an adapter are recycled. Meaning you end using items with imageAlpha = 0 even if they satisfy the if statement here:

    if (viewHolder.imageView != null) {
        if (item.getActive()) {
            viewHolder.imageView.setImageDrawable(images[item.getType()]);
        } 
        else {
           viewHolder.imageView.setImageAlpha(0);
        }
    }
    

    You should change critical properties in both parts of the statement, because of the recycling. So, it should be like:

    if (viewHolder.imageView != null) {
          if (item.getActive()) {
              viewHolder.imageView.setImageAlpha(255);
              viewHolder.imageView.setImageDrawable(images[item.getType()]);
          }
          else {
              viewHolder.imageView.setImageAlpha(0);
          }
    }
    

    Here is an image of how list items recycling works:

    enter image description here

    Source: http://android.amberfog.com/?p=296 (It also has a tutorial about lisviews)

    I also recommend to go with the viewHolder approach (faster and more efficient), there is a google talk about it (https://youtu.be/wDBM6wVEO70), and you can also use the code Claud25 provided.

    Hope it helps :)