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

RecyclerView滚动触发IndexOutOfBoundsException问题求助

解决RecyclerView滚动时的IndexOutOfBoundsException问题

你遇到的java.lang.IndexOutOfBoundsException: Invalid index 49, size is 0错误,核心原因是RecyclerView的Adapter在数据源items已经为空的情况下,仍然尝试去访问位置49的数据——这时候集合里没有任何元素,自然就抛出越界异常了。我来帮你拆解可能的原因和对应的解决办法:

一、先定位核心问题点

你的Adapter里这段代码是直接触发异常的地方:

return items.get(position) != null ? VIEW_ITEM : VIEW_PROG;

当items集合的size为0时,items.get(49)肯定会报错——因为集合里根本没有第49个元素。而RecyclerView会请求这个position,说明它还“以为”数据源里有这么多元素,这通常是数据源变更后没及时通知Adapter导致的。

二、具体解决方案

1. 数据源变更必须同步通知Adapter

每次修改items集合(清空、添加、删除元素)后,一定要调用Adapter的通知方法,让RecyclerView更新UI状态,避免它继续绑定旧的position数据。比如:

// 清空数据源时
items.clear();
rcAdapter.notifyDataSetChanged(); // 这行绝对不能少!

// 或者更新数据源时(更细粒度的通知,性能更好)
int startPosition = items.size();
items.addAll(newData);
rcAdapter.notifyItemRangeInserted(startPosition, newData.size());

如果只用items.clear()却不通知Adapter,RecyclerView还会保留之前的item数量,滚动时就会请求超出当前数据源范围的position。

2. 修复getItemViewType的安全判断逻辑

你现在的getItemViewType方法没有先判断position是否合法,直接去访问items.get(position),这在数据源为空时必然报错。修改成下面这样,先做边界检查:

@Override
public int getItemViewType(int position) {
    // 先判断position是否在当前数据源的有效范围内
    if (position >= items.size()) {
        return VIEW_PROG; // 或者根据你的逻辑返回对应的类型
    }
    // 再去判断item是否为空
    return items.get(position) != null ? VIEW_ITEM : VIEW_PROG;
}

另外,你Activity里的SpanSizeLookup中调用了rcAdapter.getItemViewType(position),这里也要确保当数据源变化时,Lookup能拿到最新的状态——最好不要在Lookup里直接依赖Adapter的方法,或者确保调用时Adapter的数据源是有效的。

3. 处理异步加载的竞态问题

如果你的数据是异步加载的(比如网络请求),很可能出现“旧的加载回调在数据源已经被清空后才执行”的情况,导致Adapter拿到过时的position。解决办法是:

  • 用生命周期绑定的方式管理异步任务,比如用CoroutineScope(Kotlin)或者Disposable(RxJava),在Activity销毁时取消旧的请求。
  • 在更新数据源前,先检查当前页面是否还处于活跃状态,避免无效的更新。

4. 检查getItemCount的返回值

如果你的Adapter有加载更多的逻辑,getItemCount可能返回items.size() + 1(多一个加载更多的item)。这时候要确保:当items为空时,不要返回1以外的数值,或者在getItemViewType里处理好边界情况,避免请求超出范围的position。

三、完整的Adapter示例片段

给你一个更安全的Adapter写法参考:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int VIEW_ITEM = 0;
    private static final int VIEW_PROG = 1;
    private List<YourItemModel> items = new ArrayList<>();
    private boolean hasMoreData = true; // 标记是否还有更多数据要加载

    // 统一的数据源更新方法,自动通知Adapter
    public void updateItems(List<YourItemModel> newItems) {
        items.clear();
        items.addAll(newItems);
        notifyDataSetChanged();
    }

    public void clearItems() {
        items.clear();
        hasMoreData = true; // 清空后重置加载状态
        notifyDataSetChanged();
    }

    public void setHasMoreData(boolean hasMore) {
        this.hasMoreData = hasMore;
        notifyDataSetChanged();
    }

    @Override
    public int getItemViewType(int position) {
        // 先做边界判断,避免越界
        if (position >= items.size()) {
            return hasMoreData ? VIEW_PROG : VIEW_ITEM; // 没有更多数据时不显示加载框
        }
        return items.get(position) != null ? VIEW_ITEM : VIEW_PROG;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        if (viewType == VIEW_ITEM) {
            return new ItemViewHolder(inflater.inflate(R.layout.item_layout, parent, false));
        } else {
            return new ProgressViewHolder(inflater.inflate(R.layout.progress_layout, parent, false));
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        // 绑定数据前再次检查position合法性
        if (position >= items.size()) {
            // 处理加载更多的逻辑,比如触发下一页请求
            return;
        }
        YourItemModel item = items.get(position);
        // 绑定item数据到ViewHolder
        ((ItemViewHolder) holder).bind(item);
    }

    @Override
    public int getItemCount() {
        // 只有在需要显示加载更多时才+1,否则直接返回items.size()
        return items.size() + (hasMoreData ? 1 : 0);
    }

    // 定义你的ViewHolder类
    private static class ItemViewHolder extends RecyclerView.ViewHolder {
        // ... ViewHolder的控件引用
        public void bind(YourItemModel item) {
            // ... 绑定逻辑,比如设置文本、图片等
        }
    }

    private static class ProgressViewHolder extends RecyclerView.ViewHolder {
        // ... 加载更多的ViewHolder控件引用,比如ProgressBar
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:02:07