BaseAdapter 没有正确地从视图中删除项目?

BaseAdapter not properly removing items from view?

我制作了一个自定义 BaseAdapter class 以在 ListView 中显示注册码。每当我向列表中添加另一个键并调用 notifyDataSetChanged() 时,ListView 的视觉更新就很好。我的数据结构只是一个包含一些字符串和一个 int 的简单结构的 ArrayList。但是,当我尝试清除键列表并调用 notifyDataSetChanged() 时,视觉上没有任何反应。当我关闭并重新打开应用程序时,该列表应该是空的。在调用 notifyDataSetChanged() 之前,我现在调用 notifyDataSetInvalidated()。这使得视觉效果在我清除键后立即正确更新,但是 Google 的 documentation 警告不要使用该方法,除非您完全使用该适配器。当我在擦除它们后注册更多键时,它们仍然显示在 ListView 中,这使得 notifyDataSetInvalidated() 似乎是一个可行的解决方案。如果我继续为 ListView 使用相同的 BaseAdapter 和 ArrayList,这种方法是否存在任何危险?有更好的解决方案吗?这是我的自定义 BaseAdapter 实现:

class KeyAdapter extends BaseAdapter implements Database.KeyRegistrationListener {

    private ArrayList<Database.KeyDetails> keyInfo;

    public KeyAdapter(ArrayList<Database.KeyDetails> keyInfo) {

        this.keyInfo = keyInfo;

    }

    @Override
    public int getCount() {

        return keyInfo.size();

    }

    @Override
    public Database.KeyDetails getItem(int position) {

        return keyInfo.get(position);

    }

    @Override
    public long getItemId(int position) {

        return position;

    }

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

        TextView userID;
        TextView appName;
        TextView date;
        TextView time;

        if (convertView == null) {

            convertView = getLayoutInflater().inflate(R.layout.registration_item, parent, false);

            userID =  (TextView) convertView.findViewById(R.id.user_id);
            appName = (TextView) convertView.findViewById(R.id.app_name);
            date =    (TextView) convertView.findViewById(R.id.date_used);
            time =    (TextView) convertView.findViewById(R.id.time_used);

            convertView.setTag(R.id.user_id, userID);
            convertView.setTag(R.id.app_name, appName);
            convertView.setTag(R.id.date_used, date);
            convertView.setTag(R.id.time_used, time);

        } else {

            userID =  (TextView) convertView.getTag(R.id.user_id);
            appName = (TextView) convertView.getTag(R.id.app_name);
            date =    (TextView) convertView.getTag(R.id.date_used);
            time =    (TextView) convertView.getTag(R.id.time_used);

        }

        Database.KeyDetails key = getItem(pos);

        userID.setText(key.userID);
        appName.setText(key.appName);
        date.setText(key.date);
        time.setText(key.time);

        return convertView;

    }

    @Override
    public void notifyKeysUpdated(Database.KeyDetails newKeyDesc) {

        if (newKeyDesc == null) {

            keyInfo.clear();
            notifyDataSetInvalidated();
            notifyDataSetChanged();

        } else{

            keyInfo.add(newKeyDesc);
            notifyDataSetChanged();

        }

    }

}

每当我向数据库添加键时,我的包装器 class 都会为 SQLite 数据库调用 notifyKeysUpdated() 方法。如果我的数据库对象提供监听器 null,这意味着数据库中的所有键都已被清除并且 ListView 应该在视觉上是空的。

编辑: 自从我开始使用 notifyDataSetInvalidated(),ListView 不再显示任何内容,包括我注册新密钥时。

在对 android 中的 BaseAdapter 和 ListView 的源代码以及这个问题 (Android - What does adapter.notifyDataSetInvalidated do?)

进行了一些研究之后

您可能要确定的第一件事是,您的数据并未完全绑定到适配器。通常BaseAdapter只是接口的集合,不包含arraylist的任何字段。这意味着大小,获取数据取决于您。要指示数据集不再有效,您应该覆盖方法 notifyDataSetInvalidated()。

我试图将您的代码复制到我的项目中。 notifyDataSetInvalidated() 和 notifyDataSetChanged() 方法都将更新整个列表,这表明 arraylist 没有完全绑定到适配器。它只是一个获取数据列表的适配器。我想知道 google 文档没有清楚地解释如何使用此方法。

最后,这个问题被证明是引用我的数据的错误。因为 Java 是一种非常安全的语言并且不允许显式指针,所以我猜想在某些时候我制作了数据对象的副本,而不是将我要更新的对象传递给 ListView。在简化我的代码并以最简单的方式创建数据并将数据传递给 table 之后,它就可以正常运行了。