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

Android中为第二个RecyclerView实现加载更多功能

Android RecyclerView 加载更多功能实现方案

我来给你一套落地性很强的加载更多实现方案,从布局到逻辑都捋得明明白白,直接就能套进你的项目里:

一、先做加载更多的FooterView布局

首先创建一个名为item_load_more.xml的布局文件,用来显示加载中、加载失败、已加载全部的状态:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="16dp">

    <ProgressBar
        android:id="@+id/loading_pb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="visible" />

    <TextView
        android:id="@+id/loading_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="加载中..." />
</LinearLayout>

二、改造你的RecyclerView Adapter

接下来要给Adapter添加多类型Item的支持,区分普通列表项和加载更多项:

1. 定义视图类型与加载状态常量

在Adapter类里添加核心常量与枚举:

private static final int ITEM_TYPE_NORMAL = 0;
private static final int ITEM_TYPE_LOAD_MORE = 1;
// 加载状态枚举,覆盖所有场景
private LoadState mLoadState = LoadState.LOADING;
public enum LoadState {
    LOADING, // 加载中
    LOAD_FAILED, // 加载失败(支持点击重试)
    LOAD_COMPLETE, // 加载完成(无更多数据)
}

2. 重写核心Adapter方法

修改getItemViewType、getItemCount来适配多类型:

@Override
public int getItemViewType(int position) {
    // 最后一个位置返回加载更多类型
    if (position == getItemCount() - 1) {
        return ITEM_TYPE_LOAD_MORE;
    } else {
        return ITEM_TYPE_NORMAL;
    }
}

@Override
public int getItemCount() {
    // 数据为空时不显示加载更多项
    return mDataList == null ? 0 : mDataList.size() + 1;
}

3. 创建对应的ViewHolder

分别实现普通项和加载更多项的ViewHolder:

// 普通项ViewHolder(替换成你自己的子项布局逻辑)
public class NormalViewHolder extends RecyclerView.ViewHolder {
    // 这里定义你的普通子项控件,比如TextView、ImageView等
    public NormalViewHolder(@NonNull View itemView) {
        super(itemView);
        // 初始化普通项控件
    }
}

// 加载更多项ViewHolder
public class LoadMoreViewHolder extends RecyclerView.ViewHolder {
    private ProgressBar mLoadingPb;
    private TextView mLoadingTv;

    public LoadMoreViewHolder(@NonNull View itemView) {
        super(itemView);
        mLoadingPb = itemView.findViewById(R.id.loading_pb);
        mLoadingTv = itemView.findViewById(R.id.loading_tv);
    }

    // 根据状态更新UI
    public void bindLoadState(LoadState loadState) {
        switch (loadState) {
            case LOADING:
                mLoadingPb.setVisibility(View.VISIBLE);
                mLoadingTv.setText("加载中...");
                itemView.setOnClickListener(null); // 加载中时取消点击
                break;
            case LOAD_FAILED:
                mLoadingPb.setVisibility(View.GONE);
                mLoadingTv.setText("加载失败,点击重试");
                // 设置重试点击事件
                itemView.setOnClickListener(v -> {
                    if (mOnRetryListener != null) {
                        mOnRetryListener.onRetry();
                    }
                });
                break;
            case LOAD_COMPLETE:
                mLoadingPb.setVisibility(View.GONE);
                mLoadingTv.setText("已加载全部");
                itemView.setOnClickListener(null);
                break;
        }
    }
}

4. 完成ViewHolder创建与数据绑定

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    if (viewType == ITEM_TYPE_LOAD_MORE) {
        View view = inflater.inflate(R.layout.item_load_more, parent, false);
        return new LoadMoreViewHolder(view);
    } else {
        // 替换成你的普通子项布局ID
        View view = inflater.inflate(R.layout.your_normal_item_layout, parent, false);
        return new NormalViewHolder(view);
    }
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    if (holder instanceof NormalViewHolder) {
        // 绑定普通项数据
        YourDataBean data = mDataList.get(position);
        // 这里写你的普通项数据填充逻辑
    } else if (holder instanceof LoadMoreViewHolder) {
        ((LoadMoreViewHolder) holder).bindLoadState(mLoadState);
    }
}

5. 添加外部调用的状态更新方法

// 更新加载状态
public void setLoadState(LoadState loadState) {
    mLoadState = loadState;
    notifyItemChanged(getItemCount() - 1);
}

// 添加新数据的方法
public void addData(List<YourDataBean> newData) {
    mDataList.addAll(newData);
    notifyDataSetChanged();
}

// 重试点击回调接口
public interface OnRetryListener {
    void onRetry();
}

private OnRetryListener mOnRetryListener;
public void setOnRetryListener(OnRetryListener listener) {
    mOnRetryListener = listener;
}

三、给RecyclerView添加滚动监听

在你的Activity/Fragment里,监听滚动事件触发加载更多:

private boolean isLoading = false; // 防止重复请求的关键标志位
private int currentPage = 1; // 当前页码

// 初始化RecyclerView
RecyclerView signsRecycler = findViewById(R.id.signsRecycler);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
signsRecycler.setLayoutManager(layoutManager);
YourAdapter adapter = new YourAdapter();
signsRecycler.setAdapter(adapter);

// 添加滚动监听
signsRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        // 只有向上滚动时才判断加载条件
        if (dy <= 0) return;

        LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
        int lastVisibleItemPos = manager.findLastVisibleItemPosition();
        int totalItemCount = manager.getItemCount();

        // 滑到最后一项且未在加载时,触发加载
        if (lastVisibleItemPos == totalItemCount - 1 && !isLoading) {
            isLoading = true;
            adapter.setLoadState(YourAdapter.LoadState.LOADING);
            loadMoreData();
        }
    }
});

// 加载更多数据的核心方法
private void loadMoreData() {
    // 这里替换成你的实际网络请求逻辑
    apiService.getSignsList(currentPage + 1)
            .enqueue(new Callback<List<YourDataBean>>() {
                @Override
                public void onResponse(Call<List<YourDataBean>> call, Response<List<YourDataBean>> response) {
                    isLoading = false;
                    if (response.isSuccessful() && response.body() != null) {
                        List<YourDataBean> newData = response.body();
                        if (newData.isEmpty()) {
                            // 无更多数据
                            adapter.setLoadState(YourAdapter.LoadState.LOAD_COMPLETE);
                        } else {
                            adapter.addData(newData);
                            currentPage++;
                            adapter.setLoadState(YourAdapter.LoadState.LOADING);
                        }
                    } else {
                        adapter.setLoadState(YourAdapter.LoadState.LOAD_FAILED);
                    }
                }

                @Override
                public void onFailure(Call<List<YourDataBean>> call, Throwable t) {
                    isLoading = false;
                    adapter.setLoadState(YourAdapter.LoadState.LOAD_FAILED);
                }
            });
}

// 设置重试监听
adapter.setOnRetryListener(() -> {
    isLoading = true;
    adapter.setLoadState(YourAdapter.LoadState.LOADING);
    loadMoreData();
});

四、额外适配与优化

  • GridLayoutManager适配:如果用网格布局,需要让加载更多项占满一行:
    GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
    gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
        @Override
        public int getSpanSize(int position) {
            return adapter.getItemViewType(position) == YourAdapter.ITEM_TYPE_LOAD_MORE ? 2 : 1;
        }
    });
    signsRecycler.setLayoutManager(gridLayoutManager);
    
  • 内存优化:页面销毁时取消未完成的网络请求,避免内存泄漏;
  • 空状态处理:初始数据为空时,可添加空状态布局提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 02:32:33