Nidhoegger
Nidhoegger

Reputation: 5232

ListView with extended Base Adapter is not updating properly

I am currently writing a custom BaseAdapter for a ListView on Android. The List Adapter basically works fine when I first insert items in the ListView, but upon inserting new Items in the middle, the wrong items are displayed after calling

notifyDataSetChanged();

Here is a simplified example (ive stripped it down to only the neccessary, if i missed to put in any information, please state a comment):

public class FriendListAdapter extends BaseAdapter {

    private ArrayList<SingleFriend> data;
    private Activity activity = null;

    private static int needAccept = -1;
    private static int friend = -2;
    private static int waiting = -3;

    public FriendListAdapter(Activity a) {
        this.activity = a;
        data = new ArrayList<>();
    }

    public void AddFriend(SingleFriend newFriend, boolean silent) {

        int insertInto = 0;

        switch (newFriend.getFriendType()) {
            case TYPE_FRIEND_WAIT_FOR_YOUR_ACCEPT:
               insertInto = needAccept;
                break;
            case TYPE_FRIEND_WAIT_FOR_HIS_ACCEPT:
                insertInto = waiting;
                break;

            case TYPE_FRIEND_ACCEPTED:
                insertInto = friend;
                break;
        }

        if (insertInto < 0) {
            boolean found = false;
            boolean inserted = false;
            int index = 0;

            do {
                if (data.get(index).getFriendID() == insertInto) {
                    found = true;
                } else if (found) {
                    if (data.get(index).getFriendID() < 0) {
                        data.add(index, newFriend);
                        inserted = true;
                    } else {
                        if (newFriend.getFriendName().compareToIgnoreCase(data.get(index).getFriendName()) < 0) {
                            data.add(index, newFriend);
                            inserted = true;
                        }
                    }
                }
                if (!inserted && index == data.size() - 1) {
                    data.add(newFriend);
                    inserted = true;
                }
                ++index;
            } while (index < data.size() && !inserted);
        }
        if (!silent) {
            notifyDataSetChanged();
        }
    }

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

        View vi = convertView;
        if (vi == null) {
            LayoutInflater inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            SingleFriend friend = (SingleFriend)getItem(position);
            if (friend != null) {
                switch (friend.getFriendType()) {
                    case TYPE_SEPARATOR:
                        vi = inflater.inflate(R.layout.friendlist_topic, null);
                        vi.setTag(friend);
                        ((TextView)vi.findViewById(R.id.friendTopic)).setText(friend.getFriendName());
                        break;
                    case TYPE_FRIEND_ACCEPTED:
                    case TYPE_FRIEND_WAIT_FOR_HIS_ACCEPT:
                    case TYPE_FRIEND_WAIT_FOR_YOUR_ACCEPT:
                        // TODO: Community rating!
                        vi = inflater.inflate(R.layout.friendlist_item, null);
                        vi.setTag(friend);
                        ((TextView)vi.findViewById(R.id.friendName)).setText(friend.getFriendName());
                        break;
                    default:
                        break;
                }
            }
        }
        return vi;
    }
}

Why is the wrong data displayed, after a Friend is inserted in the FriendList? Am I missing something? Even invalidate() does not show correct stuff (i know, invalidate is a bad option, ive just done it once for testing purposes). Thanks in advance!

Upvotes: 0

Views: 218

Answers (1)

inmyth
inmyth

Reputation: 9070

There are too many problems but I think the one you're addressing is caused (or mainly caused) by this condition check:

if (vi == null) {...

convertView in the getView method is not always null and instead recycled whereas you inflate a view for the row when it satisfies the above check. I think after you scroll up and down items will no longer be correctly associated by the content.

The other problems I noticed are,

notifyDataSetChanged is supposed to be called everytime the list the adapter is coupled with is modified. If you want to hide some elements then you should maintain two lists. The one for adapter is purely for display.

I think you want to insert items into the list based on availability of an ID. If that is the case then a much better way than looping the list looking for an ID is to use a Map like HashMap<id, SingleFriend> which can be accessed randomly by get(id).

You should first understand how getView works and use View Holder pattern correctly.

Upvotes: 1

Related Questions