RecyclerView项切换按钮栏时,不可见项触发空指针异常问题
解决RecyclerView点击项显示按钮栏时的NullPointerException问题
你遇到的空指针问题根源很明确:当之前展开的item不在RecyclerView的可见区域时,rv.getLayoutManager().findViewByPosition(pos)会返回null——因为RecyclerView会回收不可见的item以优化内存,这时候再调用findViewById()自然就会抛出NullPointerException。
要解决这个问题,我们需要抛弃直接操作View的思路,改用数据驱动UI的方式——通过记录每个item的展开状态,在onBindViewHolder中根据状态更新视图,无论item是否可见,都能正确渲染。
具体修改步骤:
1. 给数据模型添加展开状态字段
在你的PODatamodel类中新增一个布尔字段,用来记录当前item是否展开按钮栏:
public class PODatamodel { // 原有字段(title、price、date、id等) private boolean isExpanded = false; // 新增:记录是否展开按钮栏 // 新增getter和setter方法 public boolean isExpanded() { return isExpanded; } public void setExpanded(boolean expanded) { isExpanded = expanded; } }
2. 修改POAdapter的核心逻辑
替换原来的
prevPos为记录当前展开位置的变量:// 删除private int prevPos=0; private int expandedPosition = -1; // -1表示没有展开的项更新
onBindViewHolder方法,先根据数据状态设置视图可见性,再修改点击事件逻辑:@Override public void onBindViewHolder(final MyViewHolder holder, int position) { final PODatamodel item = poList.get(position); Log.d("onBindPos", " holderpos: " + holder.getAdapterPosition()); holder.tvTitle.setText(item.getTitle()); holder.tvPrice.setText(item.getPrice()); holder.tvDate.setText(item.getDate()); // 第一步:根据数据状态设置视图可见性(核心!) if (item.isExpanded()) { holder.mainSegment.setVisibility(View.GONE); holder.btnSegmnet.setVisibility(View.VISIBLE); } else { holder.mainSegment.setVisibility(View.VISIBLE); holder.btnSegmnet.setVisibility(View.GONE); } // 第二步:修改item点击事件 holder.mView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int currentPosition = holder.getAdapterPosition(); if (expandedPosition == currentPosition) { // 当前项已展开,关闭它 item.setExpanded(false); expandedPosition = -1; notifyItemChanged(currentPosition); } else { // 关闭之前展开的项(如果有的话) if (expandedPosition != -1) { PODatamodel previousItem = poList.get(expandedPosition); previousItem.setExpanded(false); notifyItemChanged(expandedPosition); } // 展开当前项 item.setExpanded(true); expandedPosition = currentPosition; notifyItemChanged(currentPosition); } } }); // btnEdit和btnDel的点击事件保持不变 holder.btnEdit.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(con, BuyActivity.class); intent.putExtra(FAPI.OPENING_MODE,FAPI.EDIT); intent.putExtra(FAPI.PO_ID,item.getId()); con.startActivity(intent); } }); holder.btnDel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String pdId = item.getId(); Log.d("onDeleteClicked", "ID :" + pdId + " userId :" + userId); pd.show(); deletePendingOrderItem(pdId, userId,holder.getAdapterPosition()); } }); }删除原来的
closePrevious方法,现在不需要直接操作View了,所有状态更新都通过数据和notifyItemChanged完成。
3. 优化删除item后的位置处理
当删除item时,如果展开的项被删除或者位置受影响,需要更新expandedPosition:
public void extractDataForDelete(String json,int pos) { try { JSONObject jsonObject = new JSONObject(json); String code = jsonObject.getString(FAPI.PO_DELETE_STATUS); if (code.equals(FAPI.DELETE_SUCCESS)) { String msg = jsonObject.getString(FAPI.PO_MESSAGE); Toast.makeText(con, msg, Toast.LENGTH_SHORT).show(); poList.remove(pos); // 调整展开位置:如果删除的是当前展开的项,重置;如果展开项在删除项之后,位置减1 if (expandedPosition == pos) { expandedPosition = -1; } else if (expandedPosition > pos) { expandedPosition--; } notifyItemRemoved(pos); // 用notifyItemRemoved比notifyDataSetChanged更高效 // notifyDataSetChanged(); 替换成上面的代码 } } catch (JSONException e) { e.printStackTrace(); } }
为什么这样修改能解决问题?
- 不再直接操作RecyclerView的View,而是通过数据模型记录状态,完全符合RecyclerView的设计理念(数据驱动UI)。
- 当item被滚出屏幕后再滚回来时,
onBindViewHolder会重新调用,根据数据模型的isExpanded状态正确渲染视图,不会出现空指针。 - 使用
notifyItemChanged只更新需要变化的item,性能比notifyDataSetChanged更好。
内容的提问来源于stack exchange,提问作者Mrinmoy
相关产品推荐
相关产品推荐

