Android RecyclerView加载更多数据的最简实现方法
RecyclerView 上拉加载更多极简实现方案
核心逻辑只有3部分:监听列表滑动判断是否滑到底部、增加加载状态标记避免重复请求、适配器支持加载中底部提示,直接基于你现有代码修改即可落地。
第一步:修改Adapter,支持加载更多底部布局
首先给适配器增加两种条目类型:普通评论项、加载中提示项,同时增加状态控制方法,修改后完整代码如下:
public class CommentAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private Context context; private List<Comments> dataList; // 条目类型常量 private static final int TYPE_COMMENT = 0; private static final int TYPE_LOADING = 1; // 加载状态标记 private boolean isLoading = false; private boolean isLastPage = false; public CommentAdapter(Context context, List<Comments> dataList) { this.context = context; this.dataList = dataList; } @Override public int getItemViewType(int position) { // 最后一项且未加载完全部数据时,显示加载中布局 return (position == dataList.size() - 1 && !isLastPage) ? TYPE_LOADING : TYPE_COMMENT; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType == TYPE_LOADING) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false); return new LoadingViewHolder(view); } // 原有普通评论项加载逻辑不变 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_comment, parent, false); return new CustomViewHolder(view); } @SuppressLint("RecyclerView") @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceof CustomViewHolder) { CustomViewHolder commentHolder = (CustomViewHolder) holder; commentHolder.txtTitle.setText(dataList.get(position).getComment()); commentHolder.txttime.setText(dataList.get(position).getUpdated_at()); commentHolder.userNametitle.setText(dataList.get(position).getuser_name()); commentHolder.itemView.setOnClickListener(view -> { // 原有点击逻辑保持不变 }); } // 加载中项不需要绑定业务数据 } @Override public int getItemCount() { // 未到最后一页时,多算1个位置给加载中布局 return isLastPage ? dataList.size() : dataList.size() + 1; } public boolean isLoading() { return isLoading; } public void setLoading(boolean loading) { isLoading = loading; } // 标记全部数据加载完成,不再显示加载中布局 public void setLastPage(boolean lastPage) { isLastPage = lastPage; notifyDataSetChanged(); } // 第一页数据替换 public void setData(List<Comments> list) { dataList.clear(); dataList.addAll(list); notifyDataSetChanged(); } // 分页数据追加 public void addData(List<Comments> newData) { int startPosition = dataList.size(); dataList.addAll(newData); notifyItemRangeInserted(startPosition, newData.size()); isLoading = false; } // 原有评论项ViewHolder保持不变 static class CustomViewHolder extends RecyclerView.ViewHolder { TextView txtTitle, txttime, userNametitle; public CustomViewHolder(@NonNull View itemView) { super(itemView); // 原有控件绑定逻辑不变 txtTitle = itemView.findViewById(R.id.txt_title); txttime = itemView.findViewById(R.id.txt_time); userNametitle = itemView.findViewById(R.id.user_name_title); } } // 加载中项ViewHolder static class LoadingViewHolder extends RecyclerView.ViewHolder { public LoadingViewHolder(@NonNull View itemView) { super(itemView); } } }
加载中布局item_loading.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:padding="16dp" android:orientation="horizontal"> <ProgressBar android:layout_width="24dp" android:layout_height="24dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="加载中..." android:layout_marginStart="8dp"/> </LinearLayout>
第二步:修改页面逻辑,增加分页加载控制
不要重复创建适配器和布局管理器,初始化一次即可,增加滑动监听触发加载下一页,修改CommentPost.java代码如下:
// 分页参数 private int currentPage = 1; private static final int PAGE_SIZE = 10; private boolean hasMore = true; private ProgressDialog progressDoalog; private RecyclerView recyclerView; private CommentAdapter adapter; private LinearLayoutManager layoutManager; private RetrofitService service; private String audioPath; // 如果做前端假分页,用这个变量存全量数据 // private List<Comments> allCommentList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_comment_post); // 原有初始化逻辑 service = RetrofitInstance.getRetrofitInstance().create(RetrofitService.class); audioPath = getIntent().getStringExtra("audio_path"); initRecyclerView(); } private void initRecyclerView() { recyclerView = findViewById(R.id.recyclerView); // 初始化空适配器 adapter = new CommentAdapter(this, new ArrayList<>()); layoutManager = new LinearLayoutManager(CommentPost.this); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(adapter); // 滑动监听,判断滑到底部触发加载 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // 只处理向下滑动的场景 if (dy <= 0) return; int totalItemCount = layoutManager.getItemCount(); int lastVisiblePos = layoutManager.findLastVisibleItemPosition(); // 满足3个条件才加载:没在加载中、还有更多数据、已经滑到最后一项 if (!adapter.isLoading() && hasMore && lastVisiblePos == totalItemCount - 1) { currentPage++; loadCommentData(); } } }); // 初始加载第一页 loadCommentData(); } private void loadCommentData() { // 第一页显示全屏加载框,后续分页靠底部加载项提示 if (currentPage == 1) { progressDoalog = new ProgressDialog(CommentPost.this); progressDoalog.setMessage("Loading...."); progressDoalog.show(); } else { adapter.setLoading(true); } // 后端支持分页就传页码和每页条数参数,不支持的话看下面注释的前端假分页逻辑 Call<List<Comments>> call = service.getComment(audioPath, currentPage, PAGE_SIZE); call.enqueue(new Callback<List<Comments>>() { @Override public void onResponse(Call<List<Comments>> call, Response<List<Comments>> response) { if (currentPage == 1) { progressDoalog.dismiss(); } List<Comments> result = response.body(); if (result == null) result = new ArrayList<>(); // 判断是否还有更多:返回数据小于每页条数说明到最后一页了 if (result.size() < PAGE_SIZE) { hasMore = false; adapter.setLastPage(true); } if (currentPage == 1) { // 第一页直接替换数据 adapter.setData(result); // 原有滚到最新评论的逻辑 recyclerView.scrollToPosition(adapter.getItemCount() - 1); // 前端假分页的话,第一次拿到全量数据后替换成以下逻辑 // allCommentList = result; // if (allCommentList.size() <= PAGE_SIZE) { // hasMore = false; // adapter.setLastPage(true); // adapter.setData(allCommentList); // } else { // adapter.setData(allCommentList.subList(0, PAGE_SIZE)); // } } else { // 追加分页数据 adapter.addData(result); // 前端假分页的话,追加数据替换成以下逻辑 // int start = (currentPage -1)*PAGE_SIZE; // int end = Math.min(currentPage*PAGE_SIZE, allCommentList.size()); // adapter.addData(allCommentList.subList(start, end)); } } @Override public void onFailure(Call<List<Comments>> call, Throwable t) { if (currentPage == 1) { progressDoalog.dismiss(); } else { adapter.setLoading(false); // 加载失败页码回退,支持重试 currentPage--; } Toast.makeText(CommentPost.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show(); } }); }
注意事项
- 如果你的后端分页参数用的是偏移量
offset而不是页码,只需要把currentPage换成offset变量,每次加载成功后offset += PAGE_SIZE即可,其他逻辑不变。 - 不要在每次加载数据时重新创建Adapter和LayoutManager,否则会出现滚动位置错乱、重复触发加载的问题。
- 如果后端返回的分页结构带总条数字段,判断是否最后一页可以用
已加载条数 >= 总条数的逻辑,比判断返回列表长度更准确。
内容的提问来源于stack exchange,提问作者Md. Shahinur Islam
相关产品推荐
相关产品推荐

