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

自定义RecyclerView Adapter中Volley请求ProgressDialog空指针问题求助

解决RecyclerView Adapter调用Activity方法引发的ProgressDialog空指针问题

兄弟,你这问题的根源太典型了——你在Adapter里直接new了一个PickingActivity实例!Android的Activity是由系统生命周期托管的,自己new出来的对象根本没有经过初始化流程,里面的pDialog自然是null,这就是空指针的罪魁祸首。而且这种方式拿到的Activity根本不是当前显示的那个页面,后续的Toast、Adapter更新也都会出问题。

下面给你一套完整的修复+优化方案:

第一步:给Adapter定义回调接口,解耦与Activity的依赖

我们让Adapter只负责触发点击事件,具体的业务逻辑交给Activity自己处理,这样既符合设计原则,又能避免创建无效Activity实例的问题。

修改你的Adapter代码:

public class PickingAdapter extends RecyclerView.Adapter<PickingAdapter.MyViewHolder> {
    // 定义回调接口,用于通知Activity处理确认事件
    public interface OnPickingItemConfirmListener {
        void onConfirmPicking(String nomorId);
    }

    private List<DataPicking> taskList;
    private OnPickingItemConfirmListener mListener;

    // 构造方法
    public PickingAdapter(List<DataPicking> taskList) {
        this.taskList = taskList;
    }

    // 给外部设置回调的方法
    public void setOnPickingItemConfirmListener(OnPickingItemConfirmListener listener) {
        this.mListener = listener;
    }

    // ... 你的MyViewHolder类保持不变 ...

    @Override 
    public void onBindViewHolder(MyViewHolder holder, final int position) { 
        final DataPicking task = taskList.get(position); 
        holder.numberid.setText(task.getNomorid()); 
        holder.nama.setText(task.getNamakonsumen()); 
        holder.rate.setText(task.getRate()); 
        holder.tanggal.setText(task.getTanggal()); 
        holder.salesman.setText(task.getSalesman()); 

        holder.cardList.setOnClickListener(new View.OnClickListener() { 
            @Override 
            public void onClick(final View view) { 
                new AlertDialog.Builder(view.getContext()) 
                    .setTitle("Proses Picking") 
                    .setMessage("Apakah kamu yakin ingin memproses picking data ini?") 
                    .setIcon(android.R.drawable.ic_dialog_alert) 
                    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { 
                        public void onClick(DialogInterface dialog, int whichButton) { 
                            String nomorid = task.getNomorid(); // 直接用当前task对象,避免position越界风险
                            // 通过回调通知Activity处理业务逻辑
                            if (mListener != null) {
                                mListener.onConfirmPicking(nomorid);
                            }
                        }
                    }) 
                    .setNegativeButton(android.R.string.no, null)
                    .show(); 
            } 
        }); 
    }

    // ... 你的getItemCount等方法保持不变 ...
}

第二步:在PickingActivity中实现回调并初始化ProgressDialog

现在回到你的Activity,先确保ProgressDialog正确初始化,然后实现Adapter的回调接口,处理请求逻辑:

public class PickingActivity extends AppCompatActivity {
    private ProgressDialog pDialog;
    private PickingAdapter mAdapter;
    private List<DataPicking> taskList;
    private static final String TAG = "PickingActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_picking);

        // 初始化ProgressDialog,绑定当前Activity上下文
        pDialog = new ProgressDialog(this);
        pDialog.setCancelable(false); // 禁止点击外部取消对话框

        // 初始化数据列表与Adapter
        taskList = new ArrayList<>();
        mAdapter = new PickingAdapter(taskList);
        // 设置Adapter的回调,处理确认事件
        mAdapter.setOnPickingItemConfirmListener(new PickingAdapter.OnPickingItemConfirmListener() {
            @Override
            public void onConfirmPicking(String nomorId) {
                kirimData(nomorId);
            }
        });

        // 给RecyclerView设置Adapter(根据你的布局ID调整)
        RecyclerView recyclerView = findViewById(R.id.recycler_view_picking);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(mAdapter);
    }

    // 修复后的kirimData方法
    public void kirimData(final String nomorid){ 
        String tag_string_req = "req_picking"; 
        pDialog.setMessage("Loading ..."); 
        showDialog(); 

        StringRequest strReq = new StringRequest(Request.Method.POST, Constants.URL_SET_PICKING, 
            new Response.Listener<String>() { 
                @Override 
                public void onResponse(String response) { 
                    Log.d(TAG, "Picking Response: " + response.toString()); 
                    hideDialog(); // 成功响应后必须隐藏对话框!
                    try { 
                        JSONObject jObj = new JSONObject(response); 
                        String status = jObj.getString("status"); 
                        if (status.equals("1")) { 
                            Toast.makeText(getApplicationContext(), "Picking berhasil diproses!", Toast.LENGTH_LONG).show();
                            // 建议重新拉取最新数据后再刷新,这里先沿用你的notifyDataSetChanged
                            mAdapter.notifyDataSetChanged(); 
                        } else { 
                            String errorMsg = jObj.getString("message"); 
                            Toast.makeText(getApplicationContext(), errorMsg, Toast.LENGTH_LONG).show(); 
                        } 
                    } catch (JSONException e) { 
                        e.printStackTrace(); 
                        Toast.makeText(getBaseContext(), "Json parsing error: " + e.getMessage(), Toast.LENGTH_LONG).show(); 
                    } 
                } 
            }, 
            new Response.ErrorListener() { 
                @Override 
                public void onErrorResponse(VolleyError error) { 
                    Log.e(TAG, "Proses Picking Error: " + error.getMessage()); 
                    Toast.makeText(getApplicationContext(), "Proses Picking gagal, coba lagi!", Toast.LENGTH_LONG).show(); 
                    hideDialog(); 
                } 
            }) { 
                @Override 
                protected Map<String, String> getParams() { 
                    Map<String, String> params = new HashMap<>(); 
                    params.put("nomorid", nomorid); 
                    return params; 
                } 
            }; 

        AppController.getInstance().addToRequestQueue(strReq, tag_string_req); 
    }

    // 封装显示和隐藏对话框的方法,避免重复代码
    private void showDialog() {
        if (!pDialog.isShowing()) {
            pDialog.show();
        }
    }

    private void hideDialog() {
        if (pDialog.isShowing()) {
            pDialog.dismiss();
        }
    }

    // 清理对话框,避免内存泄漏
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
            pDialog = null;
        }
    }
}

几个关键优化点说明

  1. 解耦设计:用接口回调替代直接创建Activity实例,让Adapter和Activity各司其职,代码更易维护和扩展。
  2. 避免position越界:在点击事件里直接用task.getNomorid(),而不是taskList.get(position),因为RecyclerView的position可能会在列表更新时发生变化。
  3. 完善对话框生命周期:在Activity销毁时清理ProgressDialog,避免内存泄漏;成功响应后也隐藏对话框,避免用户界面卡住。
  4. 用户体验提升:增加成功状态的Toast提示,错误提示更友好,让用户清楚知道操作结果。

内容的提问来源于stack exchange,提问作者DrOne

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 12:32:27