具有扩展基础适配器的 ListView 未正确更新

ListView with extended Base Adapter is not updating properly

我目前正在为 Android 上的 ListView 编写自定义 BaseAdapter。当我第一次在 ListView 中插入项目时,List Adapter 基本上工作正常,但是在中间插入新项目时,调用

后显示错误的项目
notifyDataSetChanged();

这是一个简化的例子(我把它精简到只剩下必要的部分,如果我遗漏了任何信息,请发表评论):

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;
    }
}

为什么在FriendList中插入一个Friend后,显示的数据是错误的?我错过了什么吗?甚至 invalidate() 也没有显示正确的内容(我知道,invalidate 是一个糟糕的选择,我只是出于测试目的做了一次)。提前致谢!

问题太多,但我认为您要解决的问题是(或主要是)由这种情况检查引起的:

if (vi == null) {...

convertViewgetView 方法中并不总是 null 而是被回收,而当它满足上述检查时,您会为该行膨胀视图。我认为在您向上和向下滚动后,项目将不再与内容正确关联。

我注意到的其他问题是,

notifyDataSetChanged 应该在每次修改适配器所耦合的列表时调用。如果你想隐藏一些元素,那么你应该维护两个列表。适配器的那个纯粹是为了展示。

我认为您想根据 ID 的可用性将项目插入到列表中。如果是这种情况,那么比循环列表查找 ID 更好的方法是使用像 HashMap<id, SingleFriend> 这样的映射,它可以被 get(id) 随机访问。

您应该首先了解getView 的工作原理并正确使用View Holder pattern