行为解释:使用 LiveData 通过 DiffUtil 更新适配器

behavior explanation: Using LiveData to update adapter with DiffUtil

我正在使用 LiveData 更新我的 recyclerview,如下所示:

viewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
viewModel.getPurchaseList().observe(getViewLifecycleOwner(), new Observer<List<ProductsObject>>() {
    @Override
    public void onChanged(@Nullable List<ProductsObject> productsObjects) {
        adapter.submitList(productsObjects);
        //adapter.notifyDataSetChanged();
    }
});

我正在使用 FloatActionButton 来更改 MutableLiveData 的值,如下所示:

FloatingActionButton fab = view.findViewById(R.id.cart_fab);
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        viewModel.setPurchasePrice(0, 101.2);

    }
});

所有数据都已更改,并且按预期调用了 onChanged,但它仅在我启用 adapter.notifyDataSetChanged();

时更新我的​​回收视图

如果我在 FAB 中创建一个新的 ProductsObject 提交 一个新列表,recyclerview 会在不调用 adapter.notifyDataSetChanged(); 的情况下更新,如下所示:

FloatingActionButton fab = view.findViewById(R.id.cart_fab);
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //viewModel.setPurchaseAmount(0, 101.2);
        ProductsObject prod = new ProductsObject("6666", 5, 152.2, "new product");
        List<ProductsObject> prodList = new ArrayList<>();
        prodList.add(prod);
        adapter.submitList(prodList);
    }
});

如果有人能解释原因,我将不胜感激。

这是我的适配器:

public class CartFragAdapter extends RecyclerView.Adapter<CartFragAdapter.CartFragViewHolder> {

    private static final String TAG = "debinf PurchaseAdap";

    private static final DiffUtil.ItemCallback<ProductsObject> DIFF_CALLBACK = new DiffUtil.ItemCallback<ProductsObject>() {
        @Override
        public boolean areItemsTheSame(@NonNull ProductsObject oldProduct, @NonNull ProductsObject newProduct) {
            Log.i(TAG, "areItemsTheSame: old is "+oldProduct.getCode()+" ; new is "+newProduct.getCode());
            return oldProduct.getCode().equals(newProduct.getCode());
        }

        @Override
        public boolean areContentsTheSame(@NonNull ProductsObject oldProduct, @NonNull ProductsObject newProduct) {
            Log.i(TAG, "areContentsTheSame: old is "+oldProduct.getPrice()+" ; new is "+newProduct.getPrice());
            return oldProduct.getPrice() == newProduct.getPrice();
        }
    };

    private AsyncListDiffer<ProductsObject> differ = new AsyncListDiffer<ProductsObject>(this, DIFF_CALLBACK);

    @NonNull
    @Override
    public CartFragViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_purchase, parent, false);
        return new CartFragViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull CartFragViewHolder holder, int position) {
        final ProductsObject purchaseList = differ.getCurrentList().get(position);

        holder.mCode.setText(purchaseList.getCode());
        holder.mPrice.setText(String.valueOf(purchaseList.getPrice()));
        holder.mDescription.setText(purchaseList.getDescription());


    }

    @Override
    public int getItemCount() {
        Log.i(TAG, "getItemCount");
        return differ.getCurrentList().size();
    }

    public void submitList(List<ProductsObject> products){
        Log.i(TAG, "submitList: products.size is "+products.size());
        differ.submitList(products);
    }

    public class CartFragViewHolder extends RecyclerView.ViewHolder {

        public TextView mCode, mPrice, mDescription;

        public CartFragViewHolder(@NonNull View itemView) {
            super(itemView);

            mCode = (TextView) itemView.findViewById(R.id.item_productCode);
            mPrice = (TextView) itemView.findViewById(R.id.item_productPrice);
            mDescription = (TextView) itemView.findViewById(R.id.item_productDescription);
        }
    }
}

这是我的 ViewModel:

public class MyViewModel extends ViewModel {

    MutableLiveData<List<ProductsObject>> purchaseList = new MutableLiveData<>();

    public LiveData<List<ProductsObject>> getPurchaseList() {
        return purchaseList;
    }

    public void setPurchasePrice(int position, double price) {
        List<ProductsObject> itemList = purchaseList.getValue();
        if (itemList != null && itemList.get(position) != null) {
            Log.i("debinf ViewModel", "setPurchaseAmount: "+itemList.get(position).getPrice());
            itemList.get(position).setPrice(price);
            purchaseList.postValue(itemList);
        }

    }

}

AsyncListDiffer 只保存列表的引用。这意味着如果您提交修改后的列表而不是提交新列表,AsncListDiffer 将无法检测到任何差异,因为之前的列表和新列表都引用具有相同项目的相同列表。

要解决此问题,您需要创建一个新列表和新项目。更改 MyViewModel#setPurchasePrice 如下:

    public void setPurchasePrice(int position, double price) {
        List<ProductsObject> itemList = purchaseList.getValue();
        if (itemList != null && itemList.get(position) != null) {

            List<ProductsObject> newList = new ArrayList<>();

            for (int i = 0; i < itemList.size(); i++) {
                ProductsObject prevProd = itemList.get(i);

                if (i != position) {
                    newList.add(prevProd);
                } else {
                    ProductsObject newProd = new ProductsObject(..., price, ...);
                    newList.add(newProd);
                }
            }
            purchaseList.postValue(newList);
        }

    }