Android应用运行报IndexOutOfBoundsException崩溃,附GridproductAdapter源码
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.groceryshoppingsystem, PID: 13531
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.get(ArrayList.java:437)
at com.example.groceryshoppingsystem.Adapters.GridproductAdapter.getView(GridproductAdapter.java:65)
该错误是horizontalProductModelList为空时,你尝试访问其下标为0的元素导致的。直接诱因是重写getCount()方法时写死返回4,完全没有和数据源长度绑定:
@Override public int getCount() { return 4; // 不管数据源有没有数据,系统都会尝试渲染4个item }
当horizontalProductModelList长度为0时,系统调用getView(0, ...),执行horizontalProductModelList.get(position)自然会抛出下标越界异常。
1. 优先解决崩溃问题
把getCount()返回值和数据源长度绑定,同时修复逻辑错误的getItem、getItemId方法:
@Override public int getCount() { // 数据源为空就返回0,不渲染任何item return horizontalProductModelList == null ? 0 : horizontalProductModelList.size(); } @Override public Object getItem(int position) { return horizontalProductModelList == null ? null : horizontalProductModelList.get(position); } @Override public long getItemId(int position) { return position; }
如果业务要求最多显示4个条目,可以改成:
@Override public int getCount() { if (horizontalProductModelList == null) return 0; // 取数据源长度和4的最小值,最多显示4个 return Math.min(horizontalProductModelList.size(), 4); }
2. 修复列表复用错乱问题
现有代码只有在convertView == null时才给控件赋值,列表滑动触发复用时item会显示旧数据;且控件被声明为Adapter全局成员变量,复用时会出现内容覆盖,建议改为ViewHolder模式:
@Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.horizontal_item, null); holder = new ViewHolder(); holder.container = convertView.findViewById(R.id.MainProductID); holder.productImage = convertView.findViewById(R.id.item_image); holder.producttitle = convertView.findViewById(R.id.item_title); holder.productprice = convertView.findViewById(R.id.item_Price); holder.checkBox = convertView.findViewById(R.id.check_box); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 无论是否复用都重新赋值控件内容 HorizontalProductModel currentItem = horizontalProductModelList.get(position); Picasso.get().load(currentItem.getProductimage()).into(holder.productImage); holder.producttitle.setText(currentItem.getProducttitle()); holder.productprice.setText("EGP "+currentItem.getProductprice()); boolean isfavourite = false; if (favourites != null) { for (int i = 0; i < favourites.size(); i++) { if (currentItem.getProducttitle().equals(favourites.get(i).getProducttitle())) { isfavourite = true; currentItem.setChecked(true); break; } } } if (isfavourite) { holder.checkBox.setImageResource(R.drawable.ic_baseline_favorite_24); } else { holder.checkBox.setImageResource(R.drawable.ic_baseline_favorite_shadow_24); currentItem.setChecked(false); // 避免复用时选中状态错乱 } holder.checkBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DatabaseReference ref = FirebaseDatabase.getInstance().getReference("favourites") .child(FirebaseAuth.getInstance().getCurrentUser().getUid()); if (!currentItem.isChecked()) { currentItem.setChecked(true); holder.checkBox.setImageResource(R.drawable.ic_baseline_favorite_24); ref.child(currentItem.getProducttitle()).setValue(currentItem); } else { currentItem.setChecked(false); holder.checkBox.setImageResource(R.drawable.ic_baseline_favorite_shadow_24); ref.child(currentItem.getProducttitle()).setValue(null); } } }); holder.container.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(context, ProductInfoActivity.class); intent.putExtra("Product Name",currentItem.getProducttitle()); intent.putExtra("Product Price",currentItem.getProductprice()); intent.putExtra("Product Image",currentItem.getProductimage()); intent.putExtra("Product ExpiryDate",currentItem.getExpiredDate()); intent.putExtra("Product IsFavorite",String.valueOf(currentItem.isChecked())); intent.putExtra("Is Offered","no"); context.startActivity(intent); } }); return convertView; } // 新增ViewHolder内部类 static class ViewHolder { ConstraintLayout container; ImageView productImage; TextView producttitle, productprice; ImageView checkBox; }
3. 额外优化
构造Adapter时先做空值处理,避免后续频繁判空:
public GridproductAdapter(List<HorizontalProductModel> horizontalProductModelList, List<favouritesClass> favourites, Context context) { this.horizontalProductModelList = horizontalProductModelList == null ? new ArrayList<>() : horizontalProductModelList; this.favourites = favourites == null ? new ArrayList<>() : favourites; this.context = context; }
内容的提问来源于stack exchange,提问作者user15369973

