Android가 처음인데 새로운 아키텍처 구성 요소로 바코드 판독기 시나리오를 구현하려고합니다.livedata 목록 - 어댑터가 DiffUtil 메서드로 업데이트되지 않습니다.
바코드를 읽을 때마다 바코드가 목록에 없거나 그렇지 않으면 수량을 늘리면 새 요소를 추가하는 ViewModel의 목록을 업데이트하려고합니다.
다음 솔루션이 작동하지만 UI를 업데이트하기 위해 어댑터에서 "notifyDataSetChanged"를 호출하는 것에 대해 만족스럽지 않습니다. 이는 ViewModel 목록과 어댑터 내부 목록에 동일한 객체에 대한 참조가 포함되어 있으므로 DiffUtil에서 변경 사항을 catch하지 않기 때문입니다.
UI를 업데이트하는 더 좋은 방법이 있습니까? 어댑터 옆에 아키텍처 구성 요소를 처리하기 위해 고려해야 할 사항이 있습니까?
뷰 모델
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'으로 설정된 청취자에 의해 호출됩니다 (활동 참조). 게다가'ScannerViewModel.addBarcode (String barcode)'는 옵저버에게 통지하는'scanListLD.postValue (list)'를 호출합니다. – user2692281