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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:51:12