繁体   English   中英

适配器上的notifydataSetChanged将使用新项目进行更新,但不会更新现有项目

[英]notifydataSetChanged on Adapter will update with new items, but will not update the existing items

我找不到与我的确切问题有关的具体内容,请继续阅读以了解这是什么。

我非常谨慎地确保在我的代码中的所有地方,我设置正确的只是在适配器上调用notifyDataSetChanged,我初始化itemList一次,并将其传递给适配器,并且不要重新初始化它。

它就像一个魅力,列表视图将自行更新,但仅适用于新项目。

对于现有项目,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.invalidateViews(); 然后列表视图将显示更新,但我有两个问题,有时它会闪烁,有时候,有时如果我调用它并且它在notifyDataSetChanged完成到列表视图之前运行,我得到一个“列表视图未通知数据更改“错误!

有人对这个有了解吗?

@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仍然是相同的引用,适配器将假定没有更改,因此不会重绘该视图? 或者也许适配器仅在收到通知时为新项目绘制新视图?

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

我想我需要listview刷新所有当前视图,因此, 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();
    }

我的hashcode是一个像apache这样的反射构建器(如果工作会导致基于值的哈希更改)

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

它不起作用。 据我所知,stableIds什么都不做。

编辑:

在稳定的Ids的任何组合中,这些都不起作用。 再次,和往常一样,您必须在屏幕外滚动视图然后再打开以便更新它。

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

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

ListView没有刷新已经可见的项目

对于“不稳定的ID”,如果调用notifyDatasetChanged () ,一切都应该notifyDatasetChanged () ,但似乎你的ListView不知道某些现有项目必须更新。

也许您可以尝试实现稳定的ID并以某种方式滥用它们,即id更改项目更新。

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

@Override
public boolean hasStableIds() {
    return true;
}

另一种“强力”方法是构建一个新的Adapter并为ListView设置新的适配器。

我分析了代码和你遇到的问题,我得出以下结论:

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

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

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

adapter.notifyDataSetChanged()应该执行JOB,您需要确保在适配器外部操作的List itemsList是否与Adapter在内部完全相同。 如果notifyDataSetChanged()不适合你,那肯定是这样的。

您的适配器也可能持有您在构造函数中提供的列表的“副本”,因此您对原始列表的更改不会反映...也许您可以向适配器引入一个方法,如: adapter.setItems(List items)确保项目确实设置

您不需要在ListView上调用invalidateViews()...您需要做的就是确保适配器具有要显示的正确列表并触发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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM