- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我是 Android 的新手,我正在尝试使用新架构组件实现条码阅读器场景。
每次读取条码时,如果条码不在列表中,我想更新 ViewModel 中的列表,添加新元素,否则增加数量。
以下解决方案有效,但我对在适配器上调用“notifyDataSetChanged”以更新 UI 并不满意。这是因为 ViewModel 列表和适配器内部列表包含对相同对象的引用,因此 DiffUtil 不会捕获任何更改。
是否有更好的更新 UI 的方法?除了适配器之外,我还应该考虑进行任何改进来处理架构组件吗?
View 模型
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);
}
});
}
}
}
Activity
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);
});
}
}
最佳答案
您可以使用 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);
}
}
或
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);
}
}
关于android - 实时数据列表 - 适配器不使用 DiffUtil 方法更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46771925/
我最近在/ drawable中添加了一些.gifs,以便可以将它们与按钮一起使用。这个工作正常(没有错误)。现在,当我重建/运行我的应用程序时,出现以下错误: Error: Gradle: Execu
Android 中有返回内部存储数据路径的方法吗? 我有 2 部 Android 智能手机(Samsung s2 和 s7 edge),我在其中安装了一个应用程序。我想使用位于这条路径中的 sqlit
这个问题在这里已经有了答案: What's the difference between "?android:" and "@android:" in an android layout xml f
我只想知道 android 开发手机、android 普通手机和 android root 手机之间的实际区别。 我们不能从实体店或除 android marketplace 以外的其他地方购买开发手
自Gradle更新以来,我正在努力使这个项目达到标准。这是一个团队项目,它使用的是android-apt插件。我已经进行了必要的语法更改(编译->实现和apt->注释处理器),但是编译器仍在告诉我存在
我是android和kotlin的新手,所以请原谅要解决的一个非常简单的问题! 我已经使用导航体系结构组件创建了一个基本应用程序,使用了底部的导航栏和三个导航选项。每个导航选项都指向一个专用片段,该片
我目前正在使用 Facebook official SDK for Android . 我现在正在使用高级示例应用程序,但我不知道如何让它获取应用程序墙/流/状态而不是登录的用户。 这可能吗?在那种情
我在下载文件时遇到问题, 我可以在模拟器中下载文件,但无法在手机上使用。我已经定义了上网和写入 SD 卡的权限。 我在服务器上有一个 doc 文件,如果用户单击下载。它下载文件。这在模拟器中工作正常但
这个问题在这里已经有了答案: What is the difference between gravity and layout_gravity in Android? (22 个答案) 关闭 9
任何人都可以告诉我什么是 android 缓存和应用程序缓存,因为当我们谈论缓存清理应用程序时,它的作用是,缓存清理概念是清理应用程序缓存还是像内存管理一样主存储、RAM、缓存是不同的并且据我所知,缓
假设应用程序 Foo 和 Eggs 在同一台 Android 设备上。任一应用程序都可以获取设备上所有应用程序的列表。一个应用程序是否有可能知道另一个应用程序是否已经运行以及运行了多长时间? 最佳答案
我有点困惑,我只看到了从 android 到 pc 或者从 android 到 pc 的例子。我需要制作一个从两部手机 (android) 连接的 android 应用程序进行视频聊天。我在想,我知道
用于使用 Android 以编程方式锁定屏幕。我从 Stackoverflow 之前关于此的问题中得到了一些好主意,并且我做得很好,但是当我运行该代码时,没有异常和错误。而且,屏幕没有锁定。请在这段代
文档说: android:layout_alignParentStart If true, makes the start edge of this view match the start edge
我不知道这两个属性和高度之间的区别。 以一个TextView为例,如果我将它的layout_width设置为wrap_content,并将它的width设置为50 dip,会发生什么情况? 最佳答案
这两个属性有什么关系?如果我有 android:noHistory="true",那么有 android:finishOnTaskLaunch="true" 有什么意义吗? 最佳答案 假设您的应用中有
我是新手,正在尝试理解以下 XML 代码: 查看 developer.android.com 上的文档,它说“starStyle”是 R.attr 中的常量, public static final
在下面的代码中,为什么当我设置时单选按钮的外观会发生变化 android:layout_width="fill_parent" 和 android:width="fill_parent" 我说的是
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 9
假设我有一个函数 fun myFunction(name:String, email:String){},当我调用这个函数时 myFunction('Ali', 'ali@test.com ') 如何
我是一名优秀的程序员,十分优秀!