使用ZXing扫码获取API响应时RecyclerView新增数据异常
RecyclerView扫码加载商品数据异常修复
问题场景
通过ZXing扫码器获取二维码内容后发起结算API请求,尝试将接口返回的商品数据逐次追加到RecyclerView列表中展示,但现有代码数据插入、刷新逻辑异常,无法实现预期的逐次新增效果。涉及的代码、接口返回信息如下:
适配器实现代码
public class checkoutAdapter extends RecyclerView.Adapter<checkoutAdapter.NewViewHolder>{ public ArrayList<DataCheckout> arrayList; public checkoutAdapter(ArrayList<DataCheckout> arrayList) { this.arrayList = arrayList; } @NonNull @Override public NewViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.checkout_layout, parent, false); return new NewViewHolder(view); } @Override public void onBindViewHolder(@NonNull NewViewHolder holder, int position) { holder.code.setText(arrayList.get(position).getItem_code()); holder.item.setText(arrayList.get(position).getItem_name()); holder.price.setText(arrayList.get(position).getSelling_price()); holder.quantity.setText(arrayList.get(position).getRefquantity()); } public void onRefreshAdapter(ArrayList<DataCheckout> yourList) { arrayList = yourList; notifyDataSetChanged(); } @Override public int getItemCount() { return arrayList.size(); } public class NewViewHolder extends RecyclerView.ViewHolder{ TextView code, item, price, quantity; public NewViewHolder(@NonNull View itemView) { super(itemView); code = itemView.findViewById(R.id.txtCode); item = itemView.findViewById(R.id.txtItem); price = itemView.findViewById(R.id.txtPrice); quantity = itemView.findViewById(R.id.txtQuantity); } } }
Activity实现代码
public class CheckoutActivity extends AppCompatActivity { private RecyclerView recyclerView; private checkoutAdapter adapter; DentalApi dentalApi; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_checkout); recyclerView = findViewById(R.id.rcCheckout); RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext()); recyclerView.setLayoutManager(layoutManager); Button btScan = findViewById(R.id.btnScan); btScan.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { IntentIntegrator intentIntegrator = new IntentIntegrator(CheckoutActivity.this); intentIntegrator.setPrompt("Scan a QR Code"); intentIntegrator.setOrientationLocked(true); intentIntegrator.initiateScan(); intentIntegrator.setCameraId(0); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); TextView txtView = findViewById(R.id.txtInfo); IntentResult intentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); if (intentResult != null) { if (intentResult.getContents() == null) { Toast.makeText(getBaseContext(), "Cancelled", Toast.LENGTH_SHORT).show(); } else { dentalApi = ApiClient.getRetrofit().create(DentalApi.class); dentalApi.checkout("Bearer 13|nIl6vhT8z0Yt52vLKtCUnJTdXxE1fA3B97Bv7wvq", intentResult.getContents()).enqueue(new Callback<CheckoutResponse>() { @Override public void onResponse(Call<CheckoutResponse> call, Response<CheckoutResponse> response) { CheckoutResponse chechoutResponse = null; if (response.isSuccessful()){ chechoutResponse = response.body(); if (chechoutResponse.getStatus().equals(1)); List<DataCheckout> datalist = response.body().getData(); for (int i = 0; i < datalist.size(); i++) { adapter = new checkoutAdapter((ArrayList<DataCheckout>) datalist); recyclerView.setAdapter(adapter); adapter.notifyItemInserted(datalist.size() - 1); } } } @Override public void onFailure(Call<CheckoutResponse> call, Throwable t) { } }); } } else { super.onActivityResult(requestCode, resultCode, data); } } }
API响应参考

问题根因
现有代码存在5个核心逻辑错误,直接导致列表新增效果失效:
- 适配器重复实例化:接口返回后在for循环内反复new checkoutAdapter、重复调用
recyclerView.setAdapter(),每次都会用当次返回的新列表覆盖原有列表,历史扫码的商品数据会全部丢失,无法实现累加展示 - 适配器初始化时机错误:Activity启动时从未初始化adapter实例,首次回调时直接操作未实例化的对象存在极高的空指针崩溃风险
- 刷新逻辑无效:在新创建的适配器上调用
notifyItemInserted没有实际意义,此时适配器绑定的是仅包含当次商品的全新列表,不存在“插入新条目”的场景 - 语法错误:
if (chechoutResponse.getStatus().equals(1));末尾多写了分号,导致状态判断逻辑完全失效,哪怕接口返回错误状态码也会继续执行列表加载逻辑 - 异常处理缺失:接口请求失败的回调为空,请求出错时没有任何提示,问题无法快速定位
*额外问题:扫码配置顺序错误,initiateScan()调用后才设置摄像头ID,该参数不会生效。
修复步骤
1. 改造适配器,新增数据追加方法
在原有适配器中新增单条/批量追加数据的方法,不需要每次替换整个列表:
// 新增批量追加数据方法 public void addAllItems(List<DataCheckout> items) { int insertStartPos = arrayList.size(); arrayList.addAll(items); notifyItemRangeInserted(insertStartPos, items.size()); } // 可选:新增单条数据追加方法,适配单商品扫码场景 public void addSingleItem(DataCheckout item) { arrayList.add(item); notifyItemInserted(arrayList.size() - 1); } // 原有全量刷新方法可保留,适配全量重置列表的场景 public void onRefreshAdapter(ArrayList<DataCheckout> yourList) { arrayList = yourList; notifyDataSetChanged(); }
2. 在onCreate中初始化适配器,仅绑定一次RecyclerView
在Activity创建时就初始化空数据列表和适配器,一次性给RecyclerView设置适配器,后续不再重复实例化、重复绑定,同时修正扫码参数的配置顺序:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_checkout); recyclerView = findViewById(R.id.rcCheckout); RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext()); recyclerView.setLayoutManager(layoutManager); // 初始化空列表和适配器,仅设置一次 ArrayList<DataCheckout> dataList = new ArrayList<>(); adapter = new checkoutAdapter(dataList); recyclerView.setAdapter(adapter); Button btScan = findViewById(R.id.btnScan); btScan.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { IntentIntegrator intentIntegrator = new IntentIntegrator(CheckoutActivity.this); intentIntegrator.setPrompt("Scan a QR Code"); intentIntegrator.setOrientationLocked(true); intentIntegrator.setCameraId(0); // 所有配置完成后再启动扫码 intentIntegrator.initiateScan(); } }); }
3. 改造接口回调逻辑,直接追加数据
拿到接口返回的有效数据后,直接调用适配器的追加方法新增条目,同时修复语法错误、补全异常提示:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); IntentResult intentResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); if (intentResult != null) { if (intentResult.getContents() == null) { Toast.makeText(getBaseContext(), "Cancelled", Toast.LENGTH_SHORT).show(); } else { dentalApi = ApiClient.getRetrofit().create(DentalApi.class); dentalApi.checkout("Bearer 13|nIl6vhT8z0Yt52vLKtCUnJTdXxE1fA3B97Bv7wvq", intentResult.getContents()).enqueue(new Callback<CheckoutResponse>() { @Override public void onResponse(Call<CheckoutResponse> call, Response<CheckoutResponse> response) { if (response.isSuccessful()){ CheckoutResponse checkoutResponse = response.body(); // 修复多余分号问题,增加空判断避免空指针 if (checkoutResponse != null && checkoutResponse.getStatus() == 1) { List<DataCheckout> newDataList = checkoutResponse.getData(); if (newDataList != null && newDataList.size() > 0) { // 直接追加数据到现有列表,不需要重新创建适配器 adapter.addAllItems(newDataList); // 可选:自动滚动到最新添加的条目位置 recyclerView.scrollToPosition(adapter.getItemCount() - 1); } else { Toast.makeText(CheckoutActivity.this, "未查询到对应商品信息", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(CheckoutActivity.this, "商品查询失败,请重试", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(CheckoutActivity.this, "请求异常,请检查网络", Toast.LENGTH_SHORT).show(); } } @Override public void onFailure(Call<CheckoutResponse> call, Throwable t) { Toast.makeText(CheckoutActivity.this, "网络错误:" + t.getMessage(), Toast.LENGTH_SHORT).show(); } }); } } else { super.onActivityResult(requestCode, resultCode, data); } }
编码规范提示:Java类名建议遵循大驼峰命名规则,将
checkoutAdapter重命名为CheckoutAdapter,可减少后续维护、编译阶段的歧义。
内容的提问来源于stack exchange,提问作者Jhay
相关产品推荐
相关产品推荐

