2017-10-16 15 views
1

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

    } 
} 
+0

'addBarCode'도 호출되지 만'ScannerViewModel' 생성자가'MutableLiveData'에 값을 설정하는 유일한 하나입니다. – Enzokie

+1

가독성을 위해 일부 코드는 생략했으며,'viewModel.addBarcode (barcode)'는 버튼'btnScan'으로 설정된 청취자에 의해 호출됩니다 (활동 참조). 게다가'ScannerViewModel.addBarcode (String barcode)'는 옵저버에게 통지하는'scanListLD.postValue (list)'를 호출합니다. – user2692281

답변

1

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); 
      } 
     } 
아래 그림과 같이 기존의 어댑터 PagedListAdapterHelper을 사용할 수 있습니다

또는

사용자 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) –

관련 문제