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 之后,它就可以正常运行了。
我制作了一个自定义 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 之后,它就可以正常运行了。