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

标签 java android

我找不到与我的确切问题具体相关的内容,请继续阅读以找出具体内容。

我非常小心地确保在我的代码中的任何地方,我都设置为只调用适配器上的 notifyDataSetChanged,我初始化 itemList 一次,然后将其传递给适配器,并且不重新初始化它曾经。

它就像一个魅力, ListView 会自行更新,但仅限于新项目。

对于现有项目,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 将始终更新以显示新项目,但不会更新现有项目的 View 。

这是真正的关键,告诉我这是 View 的问题:如果我调用 adapter.getItem(position); 一个更新的预先存在的项目,返回的项目将显示更新的更改(意味着 text1 和 text2 将保留它们的新值)即使它没有反射(reflect)在 ListView 中!

如果我调用 listView.invalidateViews(); 那么 ListView 将显示更新,但是我有两个问题,有时它会闪烁,有时,只是有时如果我调用它并且它在 notifyDataSetChanged 完成到达 ListView 之前运行,我收到“ 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 仍然是相同的引用,适配器会假定没有变化,因此不会重绘该 View ?或者适配器可能只在收到通知时才为新项目绘制新 View ?

要添加另一个细节,如果我向下滚动使更新的 View 离开屏幕,然后返回它,它会在 ListView 刷新/重新制作该 View 时显示正确的信息。

我想我需要 ListView 来刷新其所有当前 View ,所以 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 的反射生成器(应该会导致哈希根据值发生变化)

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

但它没有用。据我所知,stableIds 什么也没做。

编辑:

在稳定 ID 的任意组合中,这些都不起作用。再一次,与往常一样,您必须将 View 滚动到屏幕外,然后再打开才能更新。

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

最佳答案

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

ListView not refreshing already-visible items

关于java - 适配器上的 notifydataSetChanged 将更新新项目,但不会更新现有项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23666101/

相关文章:

java - Let's Encrypt + Hitch : Android OkHttp java. security.cert.CertPathValidatorException: 响应不可靠:它的有效期间隔已过期

android - 如何将此 cacheBitmap 方法与此图像获取代码集成?

Android AlarmManager 重新调度逻辑

java - 在 Spring 过滤器中更改 HttpServletRequest 的 POST 主体

android - 当 dex.force.jumbo=true 时,Dexopt 在非常大的 APK(无序 method_idx)上失败

java - 在android中运行BroadcastReceiver时出现运行时异常?

java - Runtime.exec 不工作

java - 我无法对 String 参数使用 toUpperCase() 方法,可能出了什么问题?

java - RSA加密-字节数组和字符串之间的转换

c# - Java 和 C# 中的原始类型是否不同?