自定义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; } } }
几个关键优化点说明
- 解耦设计:用接口回调替代直接创建Activity实例,让Adapter和Activity各司其职,代码更易维护和扩展。
- 避免position越界:在点击事件里直接用
task.getNomorid(),而不是taskList.get(position),因为RecyclerView的position可能会在列表更新时发生变化。 - 完善对话框生命周期:在Activity销毁时清理ProgressDialog,避免内存泄漏;成功响应后也隐藏对话框,避免用户界面卡住。
- 用户体验提升:增加成功状态的Toast提示,错误提示更友好,让用户清楚知道操作结果。
内容的提问来源于stack exchange,提问作者DrOne
相关产品推荐
相关产品推荐

