RecyclerViewの一番下までスクロールする方法は?scrollToPositionが機能しない


161

アクティビティを読み込んだ後、RecyclerViewリストの一番下までスクロールしたいと思います。

GENERIC_MESSAGE_LIST = (ArrayList) intent.getExtras().getParcelableArrayList(ConversationsAdapter.EXTRA_MESSAGE);
conversationView = (RecyclerView) findViewById(R.id.list_messages);
conversationView.setHasFixedSize(true);
conversationViewLayoutManager = new LinearLayoutManager(this);
conversationView.setLayoutManager(conversationViewLayoutManager);
conversationViewAdapter = new ConversationAdapter(GENERIC_MESSAGE_LIST, this);
conversationView.setAdapter(conversationViewAdapter);

conversationView.scrollTo(...) RecyclerViewでサポートされていないという例外をスローし、 conversationView.scrollToPosition(...)何もしないようです。

上記のコードブロックの後に、

conversationView.scrollToPosition(GENERIC_MESSAGE_LIST.size() + 1)

これは機能しません。には30個の要素がありますGENERIC_MESSAGE_LIST


scrollToPositionアダプタを設定した直後に電話していますか?
ianhanniballake 2014年

@ianhanniballakeはい、それは直後conversationView.setAdapter(conversationViewAdapter);です。
waylaidwanderer 2014年

4
これは、-1ではなく+1を呼び出しているためと考えられ、5番目の要素は0から始まるため[4]の位置になります。+ 1を実行すると、ArrayOutOfBoundsが返されます...クラッシュしませんでしたか?
RCB 2016

1
setadapter mRecyclerView.scrollToPosition(carsList.size()-1);の後に追加します。
Webserveis 2016

回答:


194

設定するsetStackFromEnd=truesetReverseLayout=trueそのLLMは、端からアイテムをレイアウトすること。

これら2つの違いは setStackFromEnd、最後の要素を表示するようにビューを設定し、レイアウトの方向は同じままであることです。(したがって、左から右への水平方向のリサイクラービューでは、最後の要素が表示され、左にスクロールすると前の要素が表示されます)

一方setReverseLayout、アダプタによって追加された要素の順序は変更されます。レイアウトは、LTRリサイクラービューの一番左にある最後の要素から始まり、次に右にスクロールすると、前の要素が表示されます。

サンプル:

final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setReverseLayout(true);
_listView.setLayoutManager(linearLayoutManager);

詳細については、ドキュメントを参照してください。


132
これは使いやすいかもしれませんが、尋ねられた質問に答えていないようです。
ジョセフ2015年

6
stackFromEnd = trueとsetReverseLayout = trueの両方を設定した場合、機能しません。誰かが回避策を知っていますか?
Luccas Correa 2016

10
チャットビューの場合、linearLayoutManager.setStackFromEnd(true)が機能します。
Muneeb Mirza

7
これはまったく問題に対処していません。
オービット

1
ビューがいっぱいになるまで機能します。それはスクロールを意図しておらず、下から積み重ねるだけです。
MiguelSlv 2017

376

私はこの投稿を見て答えを見つけましたが...この投稿の全員が私と同じシナリオに直面していたと思います:scrollToPosition()明らかな理由で完全に無視されました。

私が使っていたものは?

recyclerView.scrollToPosition(items.size());

...何がうまくいったのですか?

recyclerView.scrollToPosition(items.size() - 1);

6
recyclerView.scrollToPosition(messages.size()-1);本当に私のために働く、私は理由を疑問に思います。
エンジンバイ

25
これでうまくいきました。Javaリストはゼロベースであるため、実際には理にかなっています。例えば、リスト[0,1,2] 3の大きさを有するが、それは0から始まるので、最後の位置が2
カルバン

19
参考までに、スムーズなスクロールのために、がありsmoothScrollToPosition(...)ます。
Ivan Morgillo、2015

5
@Ivan Morgilo smoothScrollToPositionがまったく機能しない:S
15

2
@IvanMorgillo-あなたの答えは、望まれる結果とよりスムーズな= Dの両方でうまく機能するようです。スクロールして位置を
決めるの

54

私はここで答えるのが遅いことを知っています、それでも誰かが解決策を知りたいのであれば

conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() - 1);

8
それはあるべきconversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);リストの位置はゼロベースからです。
Berťák

2
最後の行の高さが画面の高さよりも高い場合、これは役に立ちません。
Anuj Jindal 2017

これは私が必要としたものです。すべてを正しく機能させるには、ハンドラを遅延後処理でラップする必要がありましたが。
Marc Alexander

18

recyclerviewの任意の位置から下にスクロールするには

edittext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                rv.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                      rv.scrollToPosition(rv.getAdapter().getItemCount() - 1);
                    }
                }, 1000);
            }
        });

15

メッセージを送信した後、サーバーからメッセージを取得する前にこのコードを追加します

recyclerView.scrollToPosition(mChatList.size() - 1);

10

を呼び出すとsetAdapter、アイテムはすぐにレイアウトされず、画面上に配置されません(単一のレイアウトパスが必要です)。したがって、scrollToPosition()呼び出し時にスクロールする実際の要素はありません。

代わりに、ViewTreeObserver.OnGlobalLayoutListenerを登録する必要があります(によって作成されたからのaddOnGlobalLayoutListner()を介しViewTreeObserverconversationView.getViewTreeObserver())。これによりscrollToPosition()、最初のレイアウトパスの後まで遅延します。

conversationView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
  public void onGlobalLayout() {
    conversationView.scrollToPosition(GENERIC_MESSAGE_LIST.size();
    // Unregister the listener to only call scrollToPosition once
    conversationView.getViewTreeObserver().removeGlobalOnLayoutListener(this);

    // Use vto.removeOnGlobalLayoutListener(this) on API16+ devices as 
    // removeGlobalOnLayoutListener is deprecated.
    // They do the same thing, just a rename so your choice.
  }
});

1
おかげで、これは動作しません。フリングが機能しないようにしてスクロールを中断し、ドラッグすると、奇妙に速くスクロールします。
waylaidwanderer 2014年

リスナーを削除していないようですね。これは正確に1回だけ(エレメントが最初にレイアウトされた直後に)呼び出す必要があり、スクロール中はまったく呼び出さないでください。
ianhanniballake 2014年

OnGlobalLayoutListenerをチェックしているため、おそらく削除されませんvto.isAlive()。理由はわかりませんが、ViewTreeObserver時々a Viewに変更されているので、確認するのではなく、isAlive最新の状態にViewTreeObserverしてくださいconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Dmitry Zaytsev

@ianhanniballakeこの問題を修正するための編集を提案しました。
Saket

6

Kotlinのソリューション:

「recyclerView.adapter」を設定した後、または「recyclerView.adapter.notifyDataSetChanged()」の後にコードを適用します

recyclerView.scrollToPosition(recyclerView.adapter.itemCount - 1)

5

コトリンでは:

recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }

private fun scrollToEnd() =
        (adapter.itemCount - 1).takeIf { it > 0 }?.let(recyclerView::smoothScrollToPosition)

はぁ、ありがとうGlobalLayoutListener!リファクタリング後、私はあなたのメソッドを使用してスクロールする必要がありました。stackoverflow.com/questions/28264139/…のようにリスナーを削除することを忘れないでください。そうしないと、RecyclerViewを最初にスクロールしません。
CoolMind

4
class MyLayoutManager extends LinearLayoutManager {

  public MyLayoutManager(Context context) {
    super(context, LinearLayoutManager.VERTICAL, false);
  }

  @Override public void smoothScrollToPosition(RecyclerView recyclerView,
      final RecyclerView.State state, final int position) {

    int fcvip = findFirstCompletelyVisibleItemPosition();
    int lcvip = findLastCompletelyVisibleItemPosition();

    if (position < fcvip || lcvip < position) {
      // scrolling to invisible position

      float fcviY = findViewByPosition(fcvip).getY();
      float lcviY = findViewByPosition(lcvip).getY();

      recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {

        int currentState = RecyclerView.SCROLL_STATE_IDLE;

        @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) {

          if (currentState == RecyclerView.SCROLL_STATE_SETTLING
              && newState == RecyclerView.SCROLL_STATE_IDLE) {

            // recursive scrolling
            smoothScrollToPosition(recyclerView, state, position);
          }

          currentState = newState;
        }

        @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

          int fcvip = findFirstCompletelyVisibleItemPosition();
          int lcvip = findLastCompletelyVisibleItemPosition();

          if ((dy < 0 && fcvip == position) || (dy > 0 && lcvip == position)) {
            // stop scrolling
            recyclerView.setOnScrollListener(null);
          }
        }
      });

      if (position < fcvip) {
        // scroll up

        recyclerView.smoothScrollBy(0, (int) (fcviY - lcviY));
      } else {
        // scroll down

        recyclerView.smoothScrollBy(0, (int) (lcviY - fcviY));
      }
    } else {
      // scrolling to visible position

      float fromY = findViewByPosition(fcvip).getY();
      float targetY = findViewByPosition(position).getY();

      recyclerView.smoothScrollBy(0, (int) (targetY - fromY));
    }
  }
}

そして

MyLayoutManager layoutManager = new MyLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);

RecyclerView.Adapter adapter = new YourAdapter();
recyclerView.setAdapter(adapter);

recyclerView.smoothScrollToPosition(adapter.getItemCount() - 1);

上記のコードは機能しますが、スムーズでなく、クールでもありません。


4

recyclerViewで接続されたアダプターがある場合は、これを実行します。

mRecyclerView.smoothScrollToPosition(mRecyclerView.getAdapter().getItemCount());


4

Rocの回答は非常に役立ちます。私はそれに小さなブロックを追加したいと思います:

mRecyclerView.scrollToPosition(mAdapter.getItemCount() - 1);

4

ビューの高さが同じではない私の場合、LayoutManagerでscrollToPositionを呼び出すと、実際に一番下までスクロールして、最後のアイテムを完全に表示できました。

recycler.getLayoutManager().scrollToPosition(adapter.getItemCount() - 1);

2

これは私にとって完全にうまくいきます:

AdapterChart adapterChart = new AdapterChart(getContext(),messageList);
recyclerView.setAdapter(adapterChart);
recyclerView.scrollToPosition(recyclerView.getAdapter().getItemCount()-1);

2

初めてリサイクル業者ビューに入るときに初めてスクロールしてから使用する

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

正の値を入力する場合は、下にスクロールする場合はマイナス、上にスクロールする場合はマイナスを入力します);

ビューの高さが非常に大きい場合、scrolltoposition特定のオフセットがビューの上部に使用され、次に使用します

int overallXScroldl =chatMessageBinding.rvChat.computeVerticalScrollOffset();
chatMessageBinding.rvChat.smoothScrollBy(0, Math.abs(overallXScroldl));

0

イアンの答えだけが私のRecyclerViewスクロールを指定された位置に動かすことができました。しかし、RecyclerViewを使用すると、その後スクロールできませんでしたscrollToPosition()smoothScrollToPosition()機能しましたが、リストが長いと、最初のアニメーションが遅くなりすぎました。その理由は、リスナーが削除されなかったからです。以下のコードを使用して電流を削除しましたがViewTreeObserver、これはチャームとして機能しました。

    mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            mRecyclerView.scrollToPosition(mPosition);
            mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
    });

0

このコードは最初に最新の投稿を提供します。この回答は役に立ちます。

    mInstaList=(RecyclerView)findViewById(R.id.insta_list);
    mInstaList.setHasFixedSize(true);

    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);

    mInstaList.setLayoutManager(layoutManager);
    layoutManager.setStackFromEnd(true);
    layoutManager.setReverseLayout(true);

0

@galexのメソッドを試しましたが、リファクタリングまで機能しました。そこで、@ yanchenkoの回答を使用して、少し変更しました。これはおそらくonCreateView()、フラグメントビューが作成された(そしておそらく適切なサイズを持っていなかった)からスクロールを呼び出したためです。

private fun scrollPhotosToEnd(view: View) {
    view.recycler_view.viewTreeObserver.addOnGlobalLayoutListener(object :
        ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                view.recycler_view.viewTreeObserver.removeOnGlobalLayoutListener(this)
            } else {
                @Suppress("DEPRECATION")
                view.recycler_view.viewTreeObserver.removeGlobalOnLayoutListener(this)
            }
            adapter?.itemCount?.takeIf { it > 0 }?.let {
                view.recycler_view.scrollToPosition(it - 1)
            }
        }
    })
}

https://stackoverflow.com/a/39001731/2914140viewTreeObserver.isAlivelikeのチェックを追加することもできます


0

これらのどれもうまくいかなかった場合、

あなたが使用しようとする必要があります:

ConstraintLayout targetView = (ConstraintLayout) recyclerView.findViewHolderForAdapterPosition(adapter.getItemCount()-1).itemView;
targetView.getParent().requestChildFocus(targetView, targetView);

これを行うことで、特定のConstraintLayout(またはあなたが持っているもの)の表示を要求しています。スクロールは瞬時です。

表示されているキーボードでも動作します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.