You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:36:13