2017-10-16 8 views
1

Je suis nouveau sur Android et j'essaie d'implémenter un scénario de lecteur de codes à barres avec les nouveaux composants d'architecture.livedata list - l'adaptateur ne se met pas à jour avec la méthode DiffUtil

Chaque fois qu'un code à barres est lu, je souhaite mettre à jour une liste dans le ViewModel en ajoutant un nouvel élément si le code à barres n'est pas présent dans la liste ou augmenter la quantité autrement.

La solution suivante fonctionne, mais cela ne me satisfait pas d'appeler "notifyDataSetChanged" sur l'adaptateur afin de mettre à jour l'interface utilisateur. En effet, la liste interne ViewModel et la liste interne des adaptateurs contiennent des références aux mêmes objets, de sorte que DiffUtil n'attrape aucune modification.

Existe-t-il un meilleur moyen de mettre à jour l'interface utilisateur? A côté de l'adaptateur, y a-t-il une amélioration à considérer pour gérer les composants d'architecture?

ViewModel

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); 
    } 
} 

Adaptateur

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); 
       } 
      }); 
     } 
    } 
} 

Activité

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' est même pas appelé et que' constructeur ScannerViewModel' est le seul réglage de la valeur à votre 'MutableLiveData'. – Enzokie

+1

Par souci de lisibilité, j'ai omis du code, 'viewModel.addBarcode (code à barres)' est appelé par le programme d'écoute sur le bouton 'btnScan' (voir Activité). De plus, 'ScannerViewModel.addBarcode (code à barres String)' appelle 'scanListLD.postValue (list)' qui avertit les observateurs. – user2692281

Répondre

1

Vous pouvez utiliser PagedListAdapterHelper dans votre adaptateur existant comme indiqué ci-dessous

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); 
      } 
     } 

OU

utilisateur 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

Un lien vers une solution est le bienvenu, mais s'il vous plaît assurez-vous que votre réponse est utile sans: [ajouter un contexte autour du lien] (// meta.stackexchange.com/a/8259) afin que vos autres utilisateurs aient une idée de ce que c'est et pourquoi c'est là, puis citez la partie la plus pertinente de la page que vous liez au cas où la page cible n'est pas disponible. [Les réponses qui sont un peu plus d'un lien peuvent être supprimées.] (// stackoverflow.com/help/deleted-answers) –