Android RecyclerView加载更多,无限滚动

时间:2020-02-23 14:29:12  来源:igfitidea点击:

在本教程中,我们将在Android应用程序中的RecyclerView上讨论并实现"无限滚动"或者"无限滚动"。
在许多应用程序(例如Facebook,Twitter)中,通常会看到无限滚动,其中无限滚动是在显示加载图标的同时从DB/Server获取下一组行。

Android RecyclerView加载更多

为了在获取下一组项目时在RecyclerView的底部显示"正在加载"图标,我们需要在RecyclerView适配器中使用"多种视图类型"。

如何实施?

通常,在简单的RecyclerView中,我们从数据结构将元素加载到适配器。

为了在RecyclerView的底部显示加载图标视图,我们需要首先在数据结构的末尾添加一个NULL元素。

为什么为NULL?

为了将该元素与其余元素区分开,并显示不同的视图类型行。

添加空值后,我们将新元素通知适配器,并获取下一组元素。

一旦获得下一组元素,我们将删除NULL元素,并将下一组元素添加到数据结构的底部。

下图演示了RecyclerView及其适配器实际发生的情况。

为了检测到用户已滚动到RecyclerView的末尾,我们需要在RecyclerView上实现OnScrollListener()

在下一节中,我们将通过填充字符串列表并使用Handlers在延迟后加载下一组列表来演示RecyclerView上的无尽滚动。

代码

下面给出了activity_main.xml布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <android.support.v7.widget.RecyclerView
      android:id="@+id/recyclerView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layoutManager="android.support.v7.widget.LinearLayoutManager"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent" 

</android.support.constraint.ConstraintLayout>

RecyclerView的行的布局在item_row.xml文件中定义,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  app:cardElevation="8dp"
  app:cardUseCompatPadding="true">

  <TextView
      android:id="@+id/tvItem"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:padding="16dp"
      android:text="Item X" 

</android.support.v7.widget.CardView>

加载视图的布局在item_loading.xml文件中定义,如下所示:

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

  <ProgressBar
      android:id="@+id/progressBar"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center_horizontal"
      android:indeterminate="true"
      android:paddingLeft="8dp"
      android:paddingRight="8dp"
      

</LinearLayout>

下面给出了RecyclerViewAdapter.java类的代码:

package com.theitroad.androidrecyclerviewloadmore;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.util.List;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

  private final int VIEW_TYPE_ITEM = 0;
  private final int VIEW_TYPE_LOADING = 1;

  public List<String> mItemList;

  public RecyclerViewAdapter(List<String> itemList) {

      mItemList = itemList;
  }

  @NonNull
  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
      if (viewType == VIEW_TYPE_ITEM) {
          View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row, parent, false);
          return new ItemViewHolder(view);
      } else {
          View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loading, parent, false);
          return new LoadingViewHolder(view);
      }
  }

  @Override
  public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {

      if (viewHolder instanceof ItemViewHolder) {

          populateItemRows((ItemViewHolder) viewHolder, position);
      } else if (viewHolder instanceof LoadingViewHolder) {
          showLoadingView((LoadingViewHolder) viewHolder, position);
      }

  }

  @Override
  public int getItemCount() {
      return mItemList == null ? 0 : mItemList.size();
  }

  /**
   * The following method decides the type of ViewHolder to display in the RecyclerView
   *
   * @param position
   * @return
   */
  @Override
  public int getItemViewType(int position) {
      return mItemList.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
  }

  private class ItemViewHolder extends RecyclerView.ViewHolder {

      TextView tvItem;

      public ItemViewHolder(@NonNull View itemView) {
          super(itemView);

          tvItem = itemView.findViewById(R.id.tvItem);
      }
  }

  private class LoadingViewHolder extends RecyclerView.ViewHolder {

      ProgressBar progressBar;

      public LoadingViewHolder(@NonNull View itemView) {
          super(itemView);
          progressBar = itemView.findViewById(R.id.progressBar);
      }
  }

  private void showLoadingView(LoadingViewHolder viewHolder, int position) {
      //ProgressBar would be displayed

  }

  private void populateItemRows(ItemViewHolder viewHolder, int position) {

      String item = mItemList.get(position);
      viewHolder.tvItem.setText(item);

  }

}

getItemViewType是我们检查List的每个元素的地方。
如果元素为NULL,则将视图类型设置为1,否则设置为0。

基于View类型,我们在onCreateViewHolder中实例化ViewHolder。

在onBindViewHolder内部,我们检查ViewHolder实例的类型并相应地填充行。

让我们看一下实例化上述适配器的MainActivity.java类。

package com.theitroad.androidrecyclerviewloadmore;

import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

  RecyclerView recyclerView;
  RecyclerViewAdapter recyclerViewAdapter;
  ArrayList<String> rowsArrayList = new ArrayList<>();

  boolean isLoading = false;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      recyclerView = findViewById(R.id.recyclerView);
      populateData();
      initAdapter();
      initScrollListener();

  }

  private void populateData() {
      int i = 0;
      while (i < 10) {
          rowsArrayList.add("Item " + i);
          i++;
      }
  }

  private void initAdapter() {

      recyclerViewAdapter = new RecyclerViewAdapter(rowsArrayList);
      recyclerView.setAdapter(recyclerViewAdapter);
  }

  private void initScrollListener() {
      recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
          @Override
          public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
              super.onScrollStateChanged(recyclerView, newState);
          }

          @Override
          public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
              super.onScrolled(recyclerView, dx, dy);

              LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();

              if (!isLoading) {
                  if (linearLayoutManager != null && linearLayoutManager.findLastCompletelyVisibleItemPosition() == rowsArrayList.size() - 1) {
                      //bottom of list!
                      loadMore();
                      isLoading = true;
                  }
              }
          }
      });

  }

  private void loadMore() {
      rowsArrayList.add(null);
      recyclerViewAdapter.notifyItemInserted(rowsArrayList.size() - 1);

      Handler handler = new Handler();
      handler.postDelayed(new Runnable() {
          @Override
          public void run() {
              rowsArrayList.remove(rowsArrayList.size() - 1);
              int scrollPosition = rowsArrayList.size();
              recyclerViewAdapter.notifyItemRemoved(scrollPosition);
              int currentSize = scrollPosition;
              int nextLimit = currentSize + 10;

              while (currentSize - 1 < nextLimit) {
                  rowsArrayList.add("Item " + currentSize);
                  currentSize++;
              }

              recyclerViewAdapter.notifyDataSetChanged();
              isLoading = false;
          }
      }, 2000);

  }
}

initScrollListener是上面代码中最重要的方法。
其中我们检查RecyclerView的滚动状态,如果最底部的项目可见,我们将显示加载视图并填充下一个列表。