适配器上的notifydataSetChanged将更新为新项目,但不会更新现有项目

我无法find与我确切的问题有关的具体内容,请继续阅读以了解具体内容。

我非常小心地确保在我的代码中的任何地方,我正确地调用适配器上的notifyDataSetChanged,我初始化itemList一次,并将其传递给适配器,并且不要重新初始化它。

它就像一个魅力,列表视图将更新自己,但只为新的项目。

对于现有的项目,ListView将不会正确更新。

例如,如果我有一个ListView显示一些自定义项目,我需要更新它,我这样做

public void updateList(List<item> newItems) { if (adapter == null) { itemList.addAll(newItems); adapter = new SomeAdapter(layoutInflator, itemList); listView.setAdapter(adapter); } else { // lets find all the duplicates and do all the updating List<item> nonDuplicateItems = new ArrayList<item>(); for (Item newItem : newItems) { boolean isDuplicate = false; for (Item oldItem : itemList) { // are these the same item? if (newItem.id == oldItem.id) { isDuplicate = true; // update the item olditem.text1 = newItem.text1; oldItem.text2 = newItem.text2; } } if (isDuplicate == false) { // add the new item nonDuplicateItems.add(newItem); } } // I have tried just adding these new ones to itemList, // but that doesnt seem to make the listview update the // views for the old ones, so I thought thuis might help // by clearing, merging, and then adding back nonDuplicateItems.addAll(itemList); itemList.clear(); itemList.addAll(nonDuplicateItems); // finally notify the adapter/listview adapter.notifyDataSetChanged(); } } 

现在listview总是会更新显示新的项目,但是它不会更新现有项目的视图。

这里是真正的踢球者告诉我这是一个问题的意见:如果我打电话adapter.getItem(position); 在已更新的已有项目上,返回的项目将显示更新后的更改(即text1和text2将保留其新值),即使它未反映在listview中!

如果我调用listView.invalidateViews(); 然后列表视图将显示更新,但我有两个问题,有时它闪烁,有时,有时,如果我调用它,它运行之前notifyDataSetChanged可以完成通过的ListView,我得到一个“列表视图未通知数据更改“错误!

有人对这个有了解吗?

 @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = layoutInflator.inflate(R.layout.item_comment, null); // when the holder is created it will find the child views // it will then call refreshHolder() on itself viewHolder = new ViewHolder(convertView, position); convertView.setTag(viewHolder); } else { viewHolder = ((ViewHolder) convertView.getTag()); viewHolder.refreshHolder(position); } return convertView; } public void refreshHolder(int position) { this.position = position; tvText1.setText(getItem(position).text1); tvText2.setText(getItem(position).text2); } 

我不知道我应该做的是重新实例化所有我的项目之前添加到列表,使用复制构造函数。 也许在通知适配器时,如果该item仍然是相同的引用,那么适配器将假定没有改变,并且因此将不重绘该视图? 或者适配器可能只在通知时为新项目绘制新视图?

要添加另一个细节,如果向下滚动使更新的视图脱离屏幕,然后再回到它,它显示正确的信息,因为列表视图刷新/重新制作该视图。

我想我需要列表视图刷新所有当前视图,所以, invalidateViews(); 可能是我必须做的。

有没有人知道更多关于这个?

编辑:这里要求是一个适配器,会有这个问题。

 public class ItemAdapter extends BaseAdapter { private final static int VIEWTYPE_PIC = 1; private final static int VIEWTYPE_NOPIC = 0; public List<Item> items; LayoutInflater layoutInflator; ActivityMain activity; public ItemAdapter(List<Item> items, LayoutInflater layoutInflator, ActivityMain activity) { super(); this.items = new ArrayList<Item>(); updateItemList(items); this.layoutInflator = layoutInflator; this.activity = activity; } public void updateItemList(List<Item> updatedItems) { if (updatedItems != null && updatedItems.size() > 0) { // FIND ALL THE DUPLICATES AND UPDATE IF NESSICARY List<Item> nonDuplicateItems = new ArrayList<Item>(); for (Item newItem : updatedItems) { boolean isDuplicate = false; for (Item oldItem : items) { if (oldItem.getId().equals(newItem.getId())) { // IF IT IS A DUPLICATE, UPDATE THE EXISTING ONE oldItem.update(newItem); isDuplicate = true; break; } } // IF IT IS NOT A DUPLICATE, ADD IT TO THE NON-DUPLICATE LIST if (isDuplicate == false) { nonDuplicateItems.add(newItem); } } // MERGE nonDuplicateItems.addAll(items); // SORT Collections.sort(nonDuplicateItems, new Item.ItemOrderComparator()); // CLEAR this.items.clear(); // ADD BACK IN this.items.addAll(nonDuplicateItems); // REFRESH notifyDataSetChanged(); } } public void removeItem(Item item) { items.remove(item); notifyDataSetChanged(); } @Override public int getCount() { if (items == null) return 0; else return items.size(); } @Override public Item getItem(int position) { if (items == null || position > getCount()) return null; else return items.get(position); } @Override public long getItemId(int position) { return getItem(position).hashCode(); } @Override public int getItemViewType(int position) { Item item = getItem(position); if (item.getPhotoURL() != null && URLUtil.isValidUrl(item.getPhotoURL()) == true) { return VIEWTYPE_PIC; } return VIEWTYPE_NOPIC; } @Override public View getView(int position, View convertView, ViewGroup parent) { ItemHolder itemHolder; if (convertView == null) { if (getItemViewType(position) == VIEWTYPE_PIC) { convertView = layoutInflator.inflate(R.layout.item_pic, null); } else { convertView = layoutInflator.inflate(R.layout.item, null); } // THIS CONSTRUCTOR ALSO CALLS REFRESH ON THE HOLDER FOR US itemHolder = new ItemHolder(convertView, position); convertView.setTag(itemHolder); } else { itemHolder = ((ItemHolder) convertView.getTag()); itemHolder.refreshHolder(position); } return convertView; } @Override public int getViewTypeCount() { return 2; } @Override public boolean hasStableIds() { return false; } @Override public boolean isEmpty() { return (getCount() < 1); } @Override public boolean areAllItemsEnabled() { return true; } @Override public boolean isEnabled(int position) { return true; } } 

好吧,我现在已经试过了

  @Override public boolean hasStableIds() { return true; } @Override public long getItemId(int position) { return getItem(position).hashCode(); } 

和这个

  @Override public boolean hasStableIds() { return false; } @Override public long getItemId(int position) { return getItem(position).hashCode(); } 

在那里我的散列码是从Apache使用像这样的reflection生成器(应该工作引起基于值的哈希更改)

  @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this); } 

并没有工作。 从我可以告诉stableIds什么都不做。

编辑:

这些工作都不是稳定的Ids的任何组合。 再一次和以往一样,您必须将视图从屏幕上滚动出来,然后重新打开,以便更新。

 listview.refreshDrawableState(); listview.requestLayout(); listview.invalidateViews(); 

Solutions Collecting From Web of "适配器上的notifydataSetChanged将更新为新项目,但不会更新现有项目"

这里有一个类似的问题,可能有效的解决scheme:

ListView不刷新已经可见的项目

对于“不稳定的ID”,如果调用notifyDatasetChanged () ,一切都应该notifyDatasetChanged () ,但是看起来你的ListView并不知道一些现有的项目已经被更新了。

也许你可以尝试实现稳定的id并以某种方式误用它们,id更改了项目更新。

 @Override public long getItemId(int position) { return getItem(position).getId(); } @Override public boolean hasStableIds() { return true; } 

另一个“蛮力”的方法是build立一个新的适配器,并为ListView设置新的适配器。

我分析了代码和问题,并得出以下结论:

refreshHolder方法仅在convertView对象在堆中具有值时才被调用。

这意味着,当convertView没有分配给任何值,不会发生更新。

解决方法是将itemHolder.refreshHolder(position)移出已插入的if-else条件块。

adapter.notifyDataSetChanged()应该执行JOB,你需要确认的是,如果你在Adapter之外操作的List itemsList是与Adapter内部持有的完全相同的实例。 如果notifyDataSetChanged()不适合你,那肯定是这样的。

您的适配器也可能持有您在构造函数中提供的列表的“副本”,所以您在原始列表中的更改将不会被反映出来……也许您可以向适配器引入一个方法,如: adapter.setItems(List物品),以确保物品真正设置

你不需要在ListView上调用invalidateViews()…所有你需要做的就是确保Adapter有正确的列表来显示和触发notifyDataSetChanged()

代替 :

 @Override public int getViewTypeCount() { return 2; } 

试试:

 @Override public int getViewTypeCount() { return getCount(); } 

而不是放置

 ViewHolder viewHolder; 

在getivew()方法中,尝试在类构造函数之前将其放在类的起始处

而不是使用convertView.set/getTag() ,为什么不直接更新视图

 refreshHolder(convertView, position); void refreshHolder(View v, int position) { ((TextView)v.findViewById(R.id.Text1)).setText(getItem(position).text1); ((TextView)v.findViewById(R.id.Text2)).setText(getItem(position).text2); } 

因为您正在重新使用视图,setTag / getTag在convertView上将不一致,并且当视图滚动出视图并返回错误的ViewHolder时,同一视图将被重用。 所以大多数时候视图没有更新

经过多次尝试和错误,这是什么工作。

 public class AutoRefreshListView extends ListView { public AutoRefreshListView(Context context) { super(context); } public AutoRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); } public AutoRefreshListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } private DataSetObserver mDataSetObserver = new AdapterDataSetObserver(); private ListAdapter mAdapter; class AdapterDataSetObserver extends DataSetObserver { @Override public void onChanged() { super.onChanged(); Log.d("AutoRefreshListView", "onChanged"); refreshVisibleViews(); } @Override public void onInvalidated() { super.onInvalidated(); Log.d("AutoRefreshListView", "onInvalidated"); refreshVisibleViews(); } } @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); if (mAdapter != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } mAdapter = adapter; mAdapter.registerDataSetObserver(mDataSetObserver); } public void refreshVisibleViews() { Log.d("AutoRefreshListView", "refresh"); if (mAdapter != null) { for (int i = getFirstVisiblePosition(); i <= getLastVisiblePosition(); i++) { final int dataPosition = i - getHeaderViewsCount(); final int childPosition = i - getFirstVisiblePosition(); if (dataPosition >= 0 && dataPosition < mAdapter.getCount() && getChildAt(childPosition) != null) { Log.d("AutoRefreshListView", "onInvalidated -> Refreshing view (data=" + dataPosition + ",child=" + childPosition + ")"); mAdapter.getView(dataPosition, getChildAt(childPosition), AutoRefreshListView.this); } } } } } 

解决办法就是从这里

ListView不刷新已经可见的项目

由Kupsef发现

或者你可以这样做: listadapter.clear(); listadapter.addAll(yourData); listadapter.clear(); listadapter.addAll(yourData);