FITSブログ

Android で RecylcerView のオーバースクロールを検知する

 Android の RecyclerView でオーバースクロールを検知する方法をまとめました。リストの末端でのオーバースクロールで最新情報を取る、ツイッターのタイムラインスクロール時のような挙動に使えますよ。

よく見るやり方(RecyclerView.addOnScrollListener)

 「RecyclerView 下端 検知」などで検索するとよく見かけるのが addOnScrollListener でスクロールを検知し、さらに canScrollVertically により上端、下端へのスクロールが可能かを調べる、というものです。

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) {
  // 上端(top edge)のオーバースクロール検知
  if (!recyclerView.canScrollVertically(-1)) {
    ...
  }
  // 下端(bottom edge)のオーバースクロール検知
  if (!recyclerView.canScrollVertically(1)) {
    ...
  }
});

 ただし、この方法だとそもそも RecyclerView の要素数が少ない場合や、要素が empty の場合には検知ができません(そもそもスクロールが発生しないとonScrolled が呼ばれないため)。また、オーバースクロールを検知して画面リロードなどが走ればよいのですが、ツイッターのように画面は遷移せずに動的に要素が増える、という場合は再スクロール時の判定が行えません(一度下端や上端に到達してしまうと、再度同じ方向にスクロールしても onScrolled が呼ばれないため)。

onScrollStateChanged でどうにかできない?

onScrollStateChanged の引数 newState には次のような値が入ってきます。

を判定することで、RecyclerView が empty だったとしてもスクロール動作が行われたかどうかを検知することは可能です。ただし、上端か下端か、という判定や、そもそもスクロール処理が onScrolled と onScrollStateChanged の二か所にまたがってしまうのは保守性を低下させます。

ListView を継承したクラスなら onOverScrolled で検知可能

android.view.View が protected なメソッドとして onOverScroll を実装していますので、ListView を継承してこの onOverScroll をオーバーライドすればオーバースクロールが検知できます。しかし、RecyclerView では onOverScroll が呼ばれませんので、この方法は使用できません。

LinerLayoutManager で検知する

 LinerLayoutManager を scrollVerticallyBy 内で、スクロール可能量と実際のスクロール量からオーバースクロールを検知することもできます。

layoutManager = new LinearLayoutManager(getContext()) {
    @Override
    public int scrollVerticallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int scrollRange = super.scrollVerticallyBy(dx, recycler, state);
        int overScroll = dx - scrollRange;
        // 5 は検知するオーバースクロール量。適宜調整する
        if (overScroll > 5) {
            // bottom edge over scroll
        } else if (overScroll < -5) {
            // top edge over scroll
        } else {
            // reset scroll state
        }
        return scrollRange;
    }
};
recyclerView.setLayoutManager(layoutManager);

 ただしこの実装ではオーバースクロールが連続して検知されますので、採用する場合はオーバースクロール検知時に Handler.postDelayed などを利用して連続したオーバースクロールを無視したほうが良いです。

仙台でAndroidのアプリ開発なら

 仙台でAndroidのアプリ開発が可能なベンダーをお探しなら、是非 FITS までご相談ください。豊富な知識できっとお役に立てることと思います。エンジニアの採用も積極的に行っていますので、もし仙台で転職を考えている方がいればこちらから応募お待ちしています。(社長の年収を超えられるインセンティブ制度があります)

モバイルバージョンを終了