54
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-03-05

初投稿します@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.javaの_Javadoc_を読むと負の数はスクロールアップを正の数はスクロールダウンをチェックするとあります。

引数に負の数を与えると先頭で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(() -> {/*要素の変更をする場合*/})); 
    }
}

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

54
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?