Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
30
Help us understand the problem. What is going on with this article?
@u_nation

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(() -> {/*要素の変更をする場合*/})); 
    }
}

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

30
Help us understand the problem. What is going on with this article?
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
u_nation

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
30
Help us understand the problem. What is going on with this article?