我是新来的Android,我试图用新的体系结构组件实施条码阅读器方案。livedata列表 - 适配器不更新与DiffUtil方法
每次读取条形码时,如果条形码不在列表中或增加数量,我想更新ViewModel中的列表以添加新元素。
以下解决方案正在工作,但它并不令我满意,因为在适配器上调用“notifyDataSetChanged”以更新UI。这是因为ViewModel列表和适配器内部列表包含对相同对象的引用,所以DiffUtil不会捕获任何更改。
有更好的方式来更新用户界面吗?在适配器旁边,我应该考虑处理体系结构组件有什么改进吗?
视图模型
public class ScannerViewModel extends ViewModel {
private MutableLiveData<List<ProductScan>> scanListLD;
public ScannerViewModel() {
scanListLD = new MutableLiveData<>();
scanListLD.setValue(new ArrayList<ProductScan>());
}
public LiveData<List<ProductScan>> getScanList() {
return scanListLD;
}
public void addBarcode(String barcode) {
List<ProductScan> list = scanListLD.getValue();
ProductScan scan = null;
for (ProductScan item : list) {
if (item.barcode.equals(barcode)) {
scan = item;
break;
}
}
if (scan == null) {
scan = new ProductScan();
scan.barcode = barcode;
scan.qt = 0;
list.add(scan);
}
scan.qt += 1;
scanListLD.postValue(list);
}
}
适配器
public class ScannerAdapter extends RecyclerView.Adapter<ScannerAdapter.ViewHolder> {
private List<ProductScan> scans;
public interface OnItemClickListener {
void onItemClick();
}
public ScannerAdapter(List<ProductScan> scans) {
this.scans = scans;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.template_scan, parent, false);
ScannerAdapter.ViewHolder viewHolder = new ScannerAdapter.ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bindData(scans.get(position), position);
}
@Override
public int getItemCount() {
return scans != null ? scans.size() : 0;
}
//Not used since scans and newScans contain same objects
public void setScans(final List<ProductScan> newScans) {
if (scans == null) {
scans = new ArrayList<ProductScan>();
scans.addAll(newScans);
notifyItemRangeInserted(0, newScans.size());
} else {
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return scans != null ? scans.size() : 0;
}
@Override
public int getNewListSize() {
return newScans != null ? newScans.size() : 0;
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
ProductScan oldScan = scans.get(oldItemPosition);
ProductScan newScan = newScans.get(newItemPosition);
return Utils.equals(oldScan.barcode,newScan.barcode);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
ProductScan oldScan = scans.get(oldItemPosition);
ProductScan newScan = newScans.get(newItemPosition);
return Utils.equals(newScan.barcode, oldScan.barcode)
&& Utils.equals(newScan.productId, oldScan.productId)
&& Utils.equals(newScan.productDescription, oldScan.productDescription)
&& Utils.equals(newScan.qt, oldScan.qt);
}
});
scans.clear();
scans.addAll(newScans);
result.dispatchUpdatesTo(this);
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TemplateScanBinding binding;
public ViewHolder(View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
}
public void bindData(ProductScan scan, int position) {
binding.setScan(scan);
binding.setBtnIncreaseQtListener(() -> {
scan.qt += 1;
notifyItemChanged(position);
});
binding.setBtnDecreaseQtListener(() -> {
scan.qt = Math.max(1, scan.qt - 1);
notifyItemChanged(position);
});
binding.edtQt.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
if (i == EditorInfo.IME_ACTION_DONE) {
binding.edtQt.clearFocus();
}
return false;
}
});
binding.edtQt.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean b) {
if (scan.qt == null || scan.qt < 1) {
scan.qt = 1;
notifyItemChanged(position);
}
InputMethodManager imm = (InputMethodManager) binding.getRoot().getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
});
}
}
}
活动
public class ScannerActivity extends ScannerBaseActivity {
@Inject
ViewModelFactory viewModelFactory;
private ScannerViewModel viewModel;
private ActivityScannerBinding binding;
private ScannerAdapter adapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication) getApplication()).getComponent().inject(this);
viewModel = ViewModelProviders.of(this, viewModelFactory).get(ScannerViewModel.class);
binding = DataBindingUtil.setContentView(this, R.layout.activity_scanner);
adapter = new ScannerAdapter(viewModel.getScanList().getValue());
binding.recvScans.setLayoutManager(new LinearLayoutManager(this));
binding.recvScans.setAdapter(adapter);
viewModel.getScanList().observe(this, (list) -> {
adapter.notifyDataSetChanged();
});
binding.btnScan.setOnClickListener((v) -> {
//calls viewModel.addBarcode(String barcode)
readBarcode(readBarcodeCallback);
});
}
}
'addBarCode'甚至没有叫,只有'ScannerViewModel'构造是唯一的值设置为你的'MutableLiveData'。 – Enzokie
为了便于阅读,我省略了一些代码,'viewModel.addBarcode(barcode)'被监听器设置为按钮'btnScan'(见Activity)调用。此外,'ScannerViewModel.addBarcode(String barcode)'调用通知观察者的'scanListLD.postValue(list)'。 – user2692281