2017-10-16 157 views
1

我是新来的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); 
     }); 

    } 
} 
+0

'addBarCode'甚至没有叫,只有'ScannerViewModel'构造是唯一的值设置为你的'MutableLiveData'。 – Enzokie

+1

为了便于阅读,我省略了一些代码,'viewModel.addBarcode(barcode)'被监听器设置为按钮'btnScan'(见Activity)调用。此外,'ScannerViewModel.addBarcode(String barcode)'调用通知观察者的'scanListLD.postValue(list)'。 – user2692281

回答

1

您可以在现有的适配器使用PagedListAdapterHelper如下图所示

class UserAdapter extends RecyclerView.Adapter<UserViewHolder> { 
    private final PagedListAdapterHelper<User> mHelper; 
    public UserAdapter(PagedListAdapterHelper.Builder<User> builder) { 
     mHelper = new PagedListAdapterHelper(this, DIFF_CALLBACK); 
    } 
    @Override 
    public int getItemCount() { 
     return mHelper.getItemCount(); 
    } 
    public void setList(PagedList<User> pagedList) { 
     mHelper.setList(pagedList); 
    } 
    @Override 
    public void onBindViewHolder(UserViewHolder holder, int position) { 
     User user = mHelper.getItem(position); 
     if (user != null) { 
      holder.bindTo(user); 
     } else { 
      // Null defines a placeholder item - PagedListAdapterHelper will automatically 
      // invalidate this row when the actual object is loaded from the database 
      holder.clear(); 
     } 
    } 
    public static final DiffCallback<User> DIFF_CALLBACK = new DiffCallback<User>() { 
      @Override 
      public boolean areItemsTheSame(
        @NonNull User oldUser, @NonNull User newUser) { 
       // User properties may have changed if reloaded from the DB, but ID is fixed 
       return oldUser.getId() == newUser.getId(); 
      } 
      @Override 
      public boolean areContentsTheSame(
        @NonNull User oldUser, @NonNull User newUser) { 
       // NOTE: if you use equals, your object must properly override Object#equals() 
       // Incorrectly returning false here will result in too many animations. 
       return oldUser.equals(newUser); 
      } 
     } 

OR

用户PageListAdapter

class UserAdapter extends PagedListAdapter<User, UserViewHolder> { 
    public UserAdapter() { 
     super(DIFF_CALLBACK); 
    } 
    @Override 
    public void onBindViewHolder(UserViewHolder holder, int position) { 
     User user = getItem(position); 
     if (user != null) { 
      holder.bindTo(user); 
     } else { 
      // Null defines a placeholder item - PagedListAdapter will automatically invalidate 
      // this row when the actual object is loaded from the database 
      holder.clear(); 
     } 
    } 
    public static final DiffCallback<User> DIFF_CALLBACK = new DiffCallback<User>() { 
     @Override 
     public boolean areItemsTheSame(
       @NonNull User oldUser, @NonNull User newUser) { 
      // User properties may have changed if reloaded from the DB, but ID is fixed 
      return oldUser.getId() == newUser.getId(); 
     } 
     @Override 
     public boolean areContentsTheSame(
       @NonNull User oldUser, @NonNull User newUser) { 
      // NOTE: if you use equals, your object must properly override Object#equals() 
      // Incorrectly returning false here will result in too many animations. 
      return oldUser.equals(newUser); 
     } 
    } 
+0

欢迎您访问解决方案的链接,但请确保您的答案在没有它的情况下很有用:[在链接附近添加上下文](// meta.stackexchange.com/a/8259),以便您的同行用户了解它是什么以及为什么它在那里,然后引用您链接的页面中最相关的部分,以防目标页面不可用。 [仅仅是一个链接的答案可能会被删除。](// stackoverflow.com/help/deleted-answers) –