You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用DiffUtil.ItemCallback时ListAdapter初始数据被覆盖问题求助

问题:RecyclerView使用submitList时旧数据被新数据替换,无法累积显示

我在应用里有两个LiveData对象,分别用来获取0-10条和11-20条数据。加载到RecyclerView的时候,本来应该显示20条,但前10条被新的10条替换了。下面是我的实现代码:

初始化代码

recyclerView = findViewById(R.id.recycler_view);
adapter = new ItemsAdapter();
recyclerView.setAdapter(adapter);
viewModel = new ViewModelProvider(this).get(ItemListViewModel.class);

获取初始数据方法

private void getInitialItems() {
    ItemListLiveData liveData = viewModel.getItemsLiveData();
    liveData.observe(this, itemtList -> adapter.submitList(itemtList));
}

获取后续数据方法

private void getNextlItems() {
    ItemListLiveData liveData = viewModel.getItemsLiveData();
    liveData.observe(this, itemtList -> adapter.submitList(itemtList));
}

ViewModel类

public class ItemListViewModel extends ViewModel {
    private ItemListRepository repository = new ItemListRepository();
    ItemListLiveData getItemsLiveData() {
        return repository.getItemListLiveData();
    }
}

仓库类仅从后端服务器获取数据。

Adapter类

public class ItemsAdapter extends ListAdapter<Item, ItemsAdapter.ItemViewHolder> {
    ItemsAdapter() {
        super(diffCallback);
    }

    @NonNull
    @Override
    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //Inflate the view
    }

    @Override
    public void onBindViewHolder(@NonNull final ItemViewHolder holder, int position) {
        //Bind the Item according to position
    }

    private static DiffUtil.ItemCallback<Item> diffCallback = new DiffUtil.ItemCallback<Item>() {
        @Override
        public boolean areItemsTheSame(@NonNull Item oldItem, @NonNull Item newItem) {
            return oldItem.id.equals(newItem.id);
        }

        @Override
        public boolean areContentsTheSame(@NonNull Item oldItem, @NonNull Item newItem) {
            return oldItem.equals(newItem);
        }
    };
}

我本来以为用了DiffUtil.ItemCallback之后,因为所有对象都是不同的,应该能得到累积的20条数据,但最后只显示了11-20这10条。请问怎么用submitList实现显示完整的20条数据?


解决方案

其实问题的核心很简单:你每次调用submitList()都是直接把新的10条数据丢给适配器,而不是把旧数据和新数据合并后再提交。DiffUtil确实能帮RecyclerView高效更新,但它不会自动帮你合并数据——它只是对比你提交的新旧两个列表,然后做对应的增删改操作。

咱们一步步来改:

  1. 在ViewModel里维护一个累积的数据集
    别再每次返回单独的新列表了,在ViewModel里存一个总的列表,拿到新数据就往里面加,再通过LiveData把完整列表发出去。比如修改你的ViewModel:

    public class ItemListViewModel extends ViewModel {
        private ItemListRepository repository = new ItemListRepository();
        private MutableLiveData<List<Item>> combinedItemsLiveData = new MutableLiveData<>();
        private List<Item> accumulatedItems = new ArrayList<>();
    
        // 加载初始的0-10条数据
        void loadInitialItems() {
            // 假设仓库里有对应的方法返回初始数据的LiveData
            repository.getInitialItemsLiveData().observeForever(newItems -> {
                accumulatedItems.clear(); // 先清空,防止重复加载
                accumulatedItems.addAll(newItems);
                // 要传新的ArrayList实例,保证LiveData能检测到变化
                combinedItemsLiveData.postValue(new ArrayList<>(accumulatedItems));
            });
        }
    
        // 加载后续的11-20条数据
        void loadNextItems() {
            // 仓库里对应的方法返回下一页数据的LiveData
            repository.getNextItemsLiveData().observeForever(newItems -> {
                accumulatedItems.addAll(newItems);
                combinedItemsLiveData.postValue(new ArrayList<>(accumulatedItems));
            });
        }
    
        // 给外部提供观察完整列表的入口
        LiveData<List<Item>> getCombinedItemsLiveData() {
            return combinedItemsLiveData;
        }
    }
    

    这里需要你调整仓库类,把原来的getItemListLiveData()拆成getInitialItemsLiveData()和getNextItemsLiveData(),分别返回对应页的LiveData。

  2. 修改Activity/Fragment里的观察逻辑
    只需要观察一次合并后的LiveData就行,不用分别调用两次观察。初始化时加载第一页,需要加载下一页时调用对应的方法:

    // 初始化代码里添加
    viewModel.getCombinedItemsLiveData().observe(this, itemList -> adapter.submitList(itemList));
    viewModel.loadInitialItems();
    
    // 比如用户滑动到底部时,调用加载下一页
    // viewModel.loadNextItems();
    
  3. 为啥之前的方式不行?
    你之前两次调用getItemsLiveData(),每次都直接把新的10条列表submitList(),这相当于告诉RecyclerView:“现在整个列表就换成这10条了”,而不是“把这10条加到原来的后面”。DiffUtil对比旧列表(10条)和新列表(10条),发现所有item的id都不一样,就会把旧的10条全删掉,再加上新的10条,自然就只显示新数据了。

另外提醒个小细节:提交列表的时候一定要传新的ArrayList实例(比如new ArrayList<>(accumulatedItems)),别直接传原来的accumulatedItems。因为LiveData默认是对比引用是否变化的,如果列表内容变了但引用没变,它可能不会触发更新。

这样调整之后,每次加载新数据都会把新内容追加到总列表里,再提交给适配器,RecyclerView就会显示完整的20条数据啦。

内容的提问来源于stack exchange,提问作者Jorn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:32:52