Help us understand the problem. What is going on with this article?

RecyclerViewでリストの先頭or末尾検知をシンプルに実装する

More than 3 years have passed since last update.

初投稿します@u_nation です。

結論

以下のコードで実現できました。

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

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        if (!recyclerView.canScrollVertically(-1)) {
           /*リストの先頭に来た時の処理*/
         }

        if (!recyclerView.canScrollVertically(1)) {
           /*リストの末尾に来た時の処理*/
        }
    }
);

重要なのはrecyclerView.canScrollVertically(-1 or 1)の部分です。
View.javaJavadocを読むと負の数はスクロールアップを正の数はスクロールダウンをチェックするとあります。

引数に負の数を与えると先頭でfalseが返り、正の数を与えると末尾でfalseを返してくれます。

View.java
/**
  * Check if this view can be scrolled vertically in a certain direction.
  *
  * @param direction Negative to check scrolling up, positive to check scrolling down.
  * @return true if this view can be scrolled in the specified direction, false otherwise.
  */
public boolean canScrollVertically(int direction) {
     final int offset = computeVerticalScrollOffset();
     final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
     if (range == 0) return false;
     if (direction < 0) {
         return offset > 0;
     } else {
         return offset < range - 1;
     }
 }

使用例

1.末尾で次のデータをロード
よくあるパターンのアレです

2.SwipeRefreshLayout
ListViewの時はリストの先頭で下方向にスワイプするとよしなに動いてくれたSwipeRefreshLayoutですがRecyclerViewではリストの先頭以外ではsetEnabled(false)してあげないと意図しない位置でもonRefreshが呼ばれてしまいます。
recyclerView.addOnScrollListener内のonScrolledメソッドの中に以下のコードで適切に処理してくれます

if (!recyclerView.canScrollVertically(-1)) {
   swipeRefreshLayout.setEnabled(true);
} else if (swipeRefreshLayout.isEnabled()) {
   swipeRefreshLayout.setEnabled(false);
}

オマケ

末尾検知はAdapter内でもできます。
positionを使ったりデータをどうこうしたい時はこちらでもいいかもしれません。
ただonBindViewHolderメソッド内でリストの要素の変更をするときは
java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
がでるのを防ぐためにHandlerを使ったほうが良いと思います。

@Override
public void onBindViewHolder(BindingHolder holder, int position) {
    if (position == getItemCount() - 1) {
       /*リストの末尾に来た時の処理*/

       handler.post(() -> {/*要素の変更をする場合*/})); 
    }
}

以上です。誤りがあれば訂正いたします(>_<)

u_nation
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした