ListView Adapter按钮点击始终获取最后一项数据的问题求助
问题原因分析
你遇到的这个问题非常典型,核心原因是Adapter中定义的全局成员变量sale和position在ListView视图复用过程中被覆盖了。
ListView的getView()方法会在滚动时复用已有的视图(convertView),每次调用getView()都会给全局的sale赋值当前行的数据。当你点击按钮时,这个全局sale已经被最后一次调用getView()时的数据覆盖了,所以不管点哪一行,拿到的都是最后一行的内容。
另外,你用来交替行背景色的altColour也是全局变量,这会导致滚动时行颜色混乱——因为复用视图时,altColour的状态会被连续的getView()调用打乱,最终行颜色不一定是奇偶交替的。
解决方案
下面是修复后的完整代码,我标注了关键修改点:
public class SalesArrayAdapter extends ArrayAdapter<SaleModel> { private Context context; // 移除全局的sale、position、altColour,改用局部变量或基于position计算 private ArrayList<SaleModel> sales; private static class ViewHolder{ RelativeLayout container; TextView tvId; TextView tvDate; TextView tvBusNo; TextView tvDriver; TextView tvNoOfTrips; TextView tvTotalExpenditure; TextView tvTotal; TextView tvAmountReceived; TextView tvStatus; TextView tvReceiptCode; ImageButton btnEdit; ImageButton btnRemove; } public SalesArrayAdapter(@NonNull Context context, @NonNull ArrayList<SaleModel> sales) { super(context, R.layout.row_sales, sales); this.context = context; this.sales = sales; } @NonNull @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public View getView(final int position, View convertView, @NonNull ViewGroup parent) { // 使用局部变量存储当前行的数据,不要用全局变量 final SaleModel currentSale = getItem(position); ViewHolder viewHolder; if (convertView == null) { viewHolder = new ViewHolder(); LayoutInflater inflater = LayoutInflater.from(getContext()); convertView = inflater.inflate(R.layout.row_sales, parent, false); // 初始化ViewHolder控件 viewHolder.container = (RelativeLayout) convertView.findViewById(R.id.row_sales_ll_container); viewHolder.tvId = (TextView) convertView.findViewById(R.id.row_sales_tv_id); viewHolder.tvDate = (TextView) convertView.findViewById(R.id.row_sales_tv_date); viewHolder.tvDriver = (TextView) convertView.findViewById(R.id.row_sales_tv_driver); viewHolder.tvNoOfTrips = (TextView) convertView.findViewById(R.id.row_sales_tv_total_no_of_trips); viewHolder.tvTotalExpenditure = (TextView) convertView.findViewById(R.id.row_sales_tv_total_expenditure); viewHolder.tvTotal = (TextView) convertView.findViewById(R.id.row_sales_tv_total); viewHolder.tvAmountReceived = (TextView) convertView.findViewById(R.id.row_sales_tv_amount_received); viewHolder.tvStatus = (TextView) convertView.findViewById(R.id.row_sales_tv_status); viewHolder.tvReceiptCode = (TextView) convertView.findViewById(R.id.row_sales_tv_receipt_code); viewHolder.btnEdit = (ImageButton) convertView.findViewById(R.id.row_sales_btn_edit); viewHolder.btnRemove = (ImageButton) convertView.findViewById(R.id.row_sales_btn_trash); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } // 基于position的奇偶性设置背景色,避免全局变量导致的颜色混乱 if (position % 2 == 0) { viewHolder.container.setBackgroundColor(Color.parseColor("#FFFFFF")); } else { viewHolder.container.setBackgroundColor(Color.parseColor("#EFEFEF")); } // 使用局部变量currentSale填充数据 viewHolder.tvId.setText(String.valueOf(currentSale.getId())); viewHolder.tvDate.setText(currentSale.getDate()); viewHolder.tvDriver.setText(currentSale.getDriver()); // 计算总行程金额(原逻辑不变,替换为currentSale) double totalTripsAmount = 0; for(int c = 0; c < currentSale.getTrips().length(); c++) { try { totalTripsAmount += currentSale.getTrips().getJSONObject(c).getDouble("trip_amount"); } catch (JSONException e) { e.printStackTrace(); } } viewHolder.tvNoOfTrips.setText("GHS "+totalTripsAmount); // 计算总支出金额(原逻辑不变,替换为currentSale) double totalExpenditureAmount = 0; for(int c = 0; c < currentSale.getExpenditure().length(); c++) { try { totalExpenditureAmount += currentSale.getExpenditure().getJSONObject(c).getDouble("amount"); } catch (JSONException e) { e.printStackTrace(); } } viewHolder.tvTotalExpenditure.setText("GHS "+totalExpenditureAmount); viewHolder.tvTotal.setText("GHS "+currentSale.getTotal()); viewHolder.tvAmountReceived.setText("GHS "+currentSale.getAmountReceived()); viewHolder.tvStatus.setText(currentSale.getIsPending() == 1 ? "Pending" : "Complete"); viewHolder.tvReceiptCode.setText(currentSale.getReceiptCode()); // 编辑按钮点击事件:使用final的currentSale和position,避免全局变量覆盖 viewHolder.btnEdit.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent editSaleIntent = new Intent(context, EditSaleActivity.class); editSaleIntent.putExtra("sale", currentSale.toJson().toString()); ((Activity) context).startActivityForResult(editSaleIntent, 800); } }); // 删除按钮点击事件:直接使用final的position,不需要通过getPosition(currentSale) viewHolder.btnRemove.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { removeSale(position); } }); return convertView; } private void removeSale(final int position) { // 先获取要删除的item的ID,避免remove后getItem(position)为空 final int saleId = getItem(position).getId(); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Confirm"); builder.setMessage("Are you sure you want to delete?"); builder.setPositiveButton("YES", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { SalesRequest salesRequest = new SalesRequest(context); // 先移除Adapter中的数据 remove(getItem(position)); notifyDataSetChanged(); // 使用提前获取的saleId调用删除接口 salesRequest.remove(saleId, mTrashOnSuccessListener, mTrashOnErrorListener); } }); builder.setNegativeButton("NO", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); AlertDialog alert = builder.create(); alert.show(); } private Response.Listener<JSONObject> mTrashOnSuccessListener = new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { // 可在这里添加删除成功后的提示操作 } }; Response.ErrorListener mTrashOnErrorListener = new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Utils.showNetworkResponse(context, error); } }; }
关键修改点说明
- 移除全局的
sale和position:改用getView()中的局部变量currentSale和final的position,确保每个行的点击事件绑定当前行的正确数据,不会被后续getView()调用覆盖。 - 修复行背景色逻辑:用
position % 2判断奇偶行,替代全局的altColour,保证滚动时行颜色始终保持正确的交替效果。 - 删除逻辑优化:提前获取要删除的sale的ID,避免调用
remove()后getItem(position)返回null导致空指针异常。 - 构造方法补全初始化:原来的构造方法未给
context和sales赋值,现在补充后避免空指针问题。
这样修改后,点击任意行的编辑或删除按钮,都会正确获取当前行的position和对应的数据,不会再出现总是选中最后一项的问题。
内容的提问来源于stack exchange,提问作者user3718908x100
相关产品推荐
相关产品推荐

