Wie scrolle ich zum Ende einer RecyclerView? scrollToPosition funktioniert nicht

160

Ich möchte nach dem Laden der Aktivität zum Ende der RecyclerView-Liste scrollen.

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(...)conversationView.scrollToPosition(...)löst eine Ausnahme aus, dass es in RecyclerView nicht unterstützt wird, und scheint nichts zu tun.

Nach dem obigen Codeblock habe ich hinzugefügt

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

was nicht funktioniert. Es gibt 30 Elemente in GENERIC_MESSAGE_LIST.

Waylaidwanderer
quelle
Rufen Sie scrollToPositionsofort nach dem Einstellen des Adapters an?
Ianhanniballake
@ianhanniballake ja, es ist gleich danach conversationView.setAdapter(conversationViewAdapter);.
Waylaidwanderer
4
Dies liegt wahrscheinlich daran, dass Sie +1 anstelle von -1 aufrufen, sodass das 5. Element Position [4] ist, da es bei 0 beginnt. Wenn Sie +1 ausführen, erhalten Sie ein ArrayOutOfBounds ... ist es dort nicht abgestürzt?
RCB
1
Nach dem Setadapter hinzufügen mRecyclerView.scrollToPosition (carsList.size () - 1);
Webserveis

Antworten:

194

Stellen Sie einfach setStackFromEnd=trueoder setReverseLayout=trueso ein, dass LLM Elemente von Ende an anordnet.

Der Unterschied zwischen diesen beiden ist der setStackFromEnd in der Ansicht das letzte Element angezeigt wird und die Layoutrichtung gleich bleibt. (In einer horizontalen Recycler-Ansicht von links nach rechts wird das letzte Element angezeigt, und wenn Sie nach links scrollen, werden die früheren Elemente angezeigt.)

Die setReverseLayoutReihenfolge der vom Adapter hinzugefügten Elemente wird geändert. Das Layout beginnt mit dem letzten Element, das in einer LTR-Recycler-Ansicht ganz links steht. Wenn Sie dann nach rechts scrollen, werden die früheren Elemente angezeigt.

Stichprobe:

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

Einzelheiten finden Sie in der Dokumentation .

Yigit
quelle
132
Dies mag etwas Nützliches sein, aber es scheint die gestellte Frage nicht zu beantworten.
Joseph
6
Wenn Sie sowohl stackFromEnd = true als auch setReverseLayout = true festlegen, funktioniert dies nicht. Kennt jemand eine Problemumgehung?
Luccas Correa
10
Für die Chat-Ansicht funktioniert linearLayoutManager.setStackFromEnd (true).
Muneeb Mirza
7
Dies spricht die Frage überhaupt nicht an.
Orbit
1
Funktioniert nur, bis die Ansicht voll ist. Es ist nicht beabsichtigt zu scrollen, sondern nur von unten zu stapeln.
MiguelSlv
376

Ich habe mir diesen Beitrag angesehen, um die Antwort zu finden, aber ... Ich glaube, jeder in diesem Beitrag hatte das gleiche Szenario wie ich: scrollToPosition()wurde aus einem offensichtlichen Grund vollständig ignoriert.

Was ich benutzt habe?

recyclerView.scrollToPosition(items.size());

... was hat funktioniert ?

recyclerView.scrollToPosition(items.size() - 1);
Roc Boronat
quelle
6
recyclerView.scrollToPosition(messages.size()-1);funktioniert wirklich für mich, ich frage mich nur den Grund.
Motor Bai
25
Das hat bei mir funktioniert. Dies ist tatsächlich sinnvoll, da Java-Listen auf Null basieren. ZB Liste [0,1,2] hat eine Größe von 3, aber die letzte Position ist 2, weil sie mit 0 beginnt.
Calvin
19
Zu Ihrer Information, für eine reibungslose Schriftrolle gibt es smoothScrollToPosition(...).
Ivan Morgillo
5
@ Ivan Morgilo SmoothScrollToPosition funktioniert überhaupt nicht: S
Cesards
2
@IvanMorgillo - Ihre Antwort scheint sowohl mit dem gewünschten Ergebnis als auch reibungsloser zu funktionieren = D. Es scheint, als wäre das Scrollen zur Position für mich ein bisschen fehlerhaft (ich vermute, dass es eine kleine Verzögerung zwischen der aufgerufenen Funktion und der Erstellung der Ansicht gibt - es funktioniert sowieso nicht richtig)
Roee
53

Ich weiß, dass es spät ist, hier zu antworten, aber wenn jemand wissen will, ob die Lösung unten ist

conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() - 1);
WritingForAnroid
quelle
7
Es sollte so sein, dass conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);Positionen in Listen auf Null basieren.
Berťák
2
Dies hilft nicht, wenn die Höhe der letzten Zeile größer als die Bildschirmhöhe ist.
Anuj Jindal
Das brauchte ich. Obwohl ich es in eine Handler-Nachverzögerung einwickeln musste, damit alles richtig funktioniert.
Marc Alexander
18

Zum Scrollen von einer beliebigen Position in der Recyclingansicht nach unten

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);
            }
        });
Muhammad Umair Shafique
quelle
15

Fügen Sie diesen Code nach dem Senden der Nachricht und vor dem Abrufen der Nachricht vom Server hinzu

recyclerView.scrollToPosition(mChatList.size() - 1);
Shubham Agrawal
quelle
10

Wenn Sie anrufen setAdapter, werden Elemente nicht sofort auf dem Bildschirm angeordnet und positioniert (dies erfordert einen einzelnen Layoutdurchlauf), sodass Ihr scrollToPosition()Anruf keine tatsächlichen Elemente enthält, zu denen Sie beim Aufrufen scrollen können.

Stattdessen sollten Sie einen ViewTreeObserver.OnGlobalLayoutListener (über addOnGlobalLayoutListner () von einem ViewTreeObservererstellten von conversationView.getViewTreeObserver()) registrieren, der Ihre Verzögerung scrollToPosition()bis nach dem ersten Layoutdurchlauf verzögert :

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.
  }
});
ianhanniballake
quelle
1
Danke, aber das funktioniert nicht. Es unterbricht das Scrollen, indem es das Schleudern von der Arbeit abbricht, und wenn Sie ziehen, scrollt es seltsam schnell.
Waylaidwanderer
Klingt so, als würden Sie den Listener nicht entfernen. Es sollte nur genau einmal aufgerufen werden (direkt nachdem die Elemente zum ersten Mal angeordnet wurden) und überhaupt nicht während des Bildlaufs.
Ianhanniballake
OnGlobalLayoutListenerwird wahrscheinlich nicht entfernt, weil Sie nach suchen vto.isAlive(). Ich kenne den Grund nicht, aber ViewTreeObservermanchmal wird es für a geändert View, also anstatt nach isAlivedir zu suchen, werde einfach besser ViewTreeObservermitconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Dmitry Zaytsev
@ianhanniballake Ich habe eine Bearbeitung vorgeschlagen, um dieses Problem zu beheben.
Saket
6

Lösung für Kotlin :

Wenden Sie den folgenden Code an, nachdem Sie "recyclerView.adapter" oder "recyclerView.adapter.notifyDataSetChanged ()" festgelegt haben.

recyclerView.scrollToPosition(recyclerView.adapter.itemCount - 1)
AbhinayMe
quelle
5

In Kotlin:

recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }

private fun scrollToEnd() =
        (adapter.itemCount - 1).takeIf { it > 0 }?.let(recyclerView::smoothScrollToPosition)
yanchenko
quelle
Hah, danke für GlobalLayoutListener! Nach dem Refactoring musste ich Ihre Methode zum Scrollen verwenden. Vergessen Sie nicht, den Listener wie in stackoverflow.com/questions/28264139/… zu entfernen, sonst scrollen Sie RecyclerView nicht zurück zum Anfang.
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));
    }
  }
}

und

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

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

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

Der obige Code funktioniert, ist aber nicht flüssig und nicht cool.

Yuki Yoshida
quelle
4

Wenn Sie einen Adapter an recyclerView angeschlossen haben, tun Sie dies einfach.

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

Wasim Amin
quelle
4

Roc Antwort ist eine große Hilfe. Ich möchte einen kleinen Block hinzufügen:

mRecyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
abedfar
quelle
4

In meinem Fall, in dem Ansichten nicht dieselbe Höhe haben, hat der Aufruf von scrollToPosition im LayoutManager dazu beigetragen , wirklich nach unten zu scrollen und das letzte Element vollständig anzuzeigen :

recycler.getLayoutManager().scrollToPosition(adapter.getItemCount() - 1);
Galex
quelle
2

Das funktioniert bei mir einwandfrei:

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

Scrollen Sie zum ersten Mal, wenn Sie zum ersten Mal in die Recycler-Ansicht wechseln, und verwenden Sie dann

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

Minus eingeben für Bildlauf nach unten für Bildlauf nach oben (positiver Wert);

Wenn die Ansicht sehr groß ist, wird für den oberen Bereich der Ansicht ein bestimmter Versatz für die Bildlaufposition verwendet

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

Nur Ians Antwort konnte meine RecyclerViewSchriftrolle an eine bestimmte Position bringen. Allerdings konnte The RecyclerViewdanach nicht mehr scrollen, als ich es benutzte scrollToPosition(). smoothScrollToPosition()funktionierte, aber die anfängliche Animation machte es zu langsam, wenn die Liste lang war. Der Grund war, dass der Hörer nicht entfernt wurde. Ich habe den folgenden Code verwendet, um den Strom zu entfernen, ViewTreeObserverund es hat als Zauber gewirkt.

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

Dieser Code gibt Ihnen zuerst den neuesten Beitrag. Ich denke, diese Antwort ist hilfreich.

    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);
hasanga lakdinu
quelle
0

Versuchte eine Methode von @galex , es funktionierte bis zum Refactoring. Also habe ich eine Antwort von @yanchenko verwendet und mich ein bisschen verändert. Wahrscheinlich liegt das daran, dass ich das Scrollen von onCreateView()dort aus aufgerufen habe , wo eine Fragmentansicht erstellt wurde (und wahrscheinlich nicht die richtige Größe hatte).

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)
            }
        }
    })
}

Sie können auch unter https://stackoverflow.com/a/39001731/2914140 einen Check von viewTreeObserver.isAlivelike hinzufügen .

CoolMind
quelle
0

Wenn nichts davon funktioniert hat,

Sie sollten versuchen zu verwenden:

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

Auf diese Weise fordern Sie die Anzeige eines bestimmten ConstraintLayout (oder was auch immer Sie haben) an. Die Schriftrolle ist sofort.

Ich arbeite sogar mit der gezeigten Tastatur.

Arnaud
quelle