¿Cómo desplazarse hasta la parte inferior de un RecyclerView? scrollToPosition no funciona


161

Me gustaría desplazarme al final de la lista RecyclerView después de cargar la actividad.

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(...)lanza una excepción sobre no ser compatible con RecyclerView, y conversationView.scrollToPosition(...)no parece hacer nada.

Después del bloque de código anterior, agregué

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

que no funciona Hay 30 elementos en GENERIC_MESSAGE_LIST.


¿Llama scrollToPositioninmediatamente después de configurar el adaptador?
ianhanniballake

@ianhanniballake sí, es justo después conversationView.setAdapter(conversationViewAdapter);.
waylaidwanderer

44
Probablemente es porque está llamando a +1 en lugar de -1, por lo que el quinto elemento sería la posición [4] porque comienza en 0. Hacer +1 le daría un ArrayOutOfBounds ... ¿no se bloqueó allí?
RCB

1
Agregar después de setadapter mRecyclerView.scrollToPosition (carsList.size () - 1);
Webserveis

Respuestas:


194

Simplemente establezca setStackFromEnd=trueo más setReverseLayout=truepara que LLM diseñe los elementos desde el final.

La diferencia entre estos dos es que setStackFromEndestablecerá la vista para mostrar el último elemento, la dirección de diseño seguirá siendo la misma. (Por lo tanto, en una vista de reciclador horizontal de izquierda a derecha, se mostrará el último elemento y el desplazamiento hacia la izquierda mostrará los elementos anteriores)

Mientras setReverseLayoutque cambiará el orden de los elementos agregados por el Adaptador. El diseño comenzará desde el último elemento, que será el más a la izquierda en una Vista de Reciclador LTR y luego, al desplazarse hacia la derecha, se mostrarán los elementos anteriores.

Muestra:

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

Consulte la documentación para más detalles.


132
Esto podría ser algo útil, pero no parece responder a la pregunta que se hizo.
Joseph

66
Si establece stackFromEnd = true y setReverseLayout = true, no funciona. ¿Alguien sabe alguna solución?
Luccas Correa

10
Para la vista de chat, linearLayoutManager.setStackFromEnd (true) funciona.
Muneeb Mirza

77
Esto no aborda la pregunta en absoluto.
Orbit

1
Solo funciona hasta que la vista esté llena. No intenta desplazarse, solo apilar desde la parte inferior.
MiguelSlv

376

Estaba mirando esta publicación para encontrar la respuesta, pero ... Creo que todos en esta publicación se enfrentaban al mismo escenario que yo: scrollToPosition()fue completamente ignorado, por una razón evidente.

¿Qué estaba usando?

recyclerView.scrollToPosition(items.size());

... ¿qué funcionó ?

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

66
recyclerView.scrollToPosition(messages.size()-1);realmente funciona para mí, solo me pregunto la razón.
Motor Bai

25
Esto funcionó para mí. En realidad tiene sentido porque las listas Java están basadas en cero. Por ejemplo, la lista [0,1,2] tiene un tamaño de 3 pero la última posición es 2 porque comienza con 0.
Calvin

19
Para su información, para un desplazamiento suave, hay smoothScrollToPosition(...).
Ivan Morgillo

55
@Ivan Morgilo smoothScrollToPosition no funciona en absoluto: S
cesards

2
@IvanMorgillo: su respuesta parece funcionar mejor tanto con el resultado deseado como más suave = D. Parece que el desplazamiento a la posición es un poco defectuoso para mí (supongo que hay un pequeño retraso entre la función llamada y la creación de la vista, de todos modos no funciona correctamente)
Roee

54

Sé que es tarde para responder aquí, aún si alguien quiere saber la solución está por debajo

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

8
Debe ser conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);como las posiciones en las listas están basadas en cero.
Berťák

2
Esto no ayudará si la altura de la última fila es mayor que la altura de la pantalla.
Anuj Jindal

Esto es lo que necesitaba. Aunque tuve que envolverlo en un controlador posterior al retraso para que todo funcionara correctamente.
Marc Alexander

18

Para desplazarse hacia abajo desde cualquier posición en la vista de reciclador hacia abajo

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

Agregue este código después de enviar el mensaje y antes de recibir el mensaje del servidor

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

10

Cuando llama setAdapter, eso no establece y coloca inmediatamente los elementos en la pantalla (que requiere una sola pasada de diseño), por lo tanto, su scrollToPosition()llamada no tiene elementos reales a los que desplazarse cuando la llama.

En su lugar, debe registrar un ViewTreeObserver.OnGlobalLayoutListener (a través de addOnGlobalLayoutListner () de un ViewTreeObservercreado por conversationView.getViewTreeObserver()) que retrasa su scrollToPosition()hasta después del primer pase de diseño:

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
Gracias, pero esto no funciona. Detiene el desplazamiento al detener el funcionamiento del lanzamiento y, si lo arrastra, se desplaza de forma extrañamente rápida.
waylaidwanderer

Parece que no estás eliminando al oyente. Solo debe llamarse exactamente una vez (justo después de que los elementos se presentan por primera vez) y nada durante el desplazamiento.
ianhanniballake

OnGlobalLayoutListenerprobablemente no se elimine porque lo está buscando vto.isAlive(). No sé el motivo, pero a ViewTreeObserverveces se cambia por un View, así que en lugar de isAlivebuscarlo mejor, simplemente póngase al día ViewTreeObserverconconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Dmitry Zaytsev

@ianhanniballake He propuesto una edición para solucionar este problema.
Saket

6

Solución para Kotlin :

aplique el siguiente código después de configurar "recyclerView.adapter" o después de "recyclerView.adapter.notifyDataSetChanged ()"

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

5

En Kotlin:

recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }

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

Ja, gracias por GlobalLayoutListener! Después de refactorizar, tuve que usar su método para desplazarme. No olvide eliminar el oyente como en stackoverflow.com/questions/28264139/… , o no desplazará RecyclerView de regreso al principio.
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));
    }
  }
}

y

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

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

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

el código anterior funciona, pero no es suave ni genial.


4

SI tiene un adaptador conectado con recyclerView, simplemente haga esto.

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


4

Roc answer es de gran ayuda. Me gustaría agregarle un pequeño bloque:

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

4

En mi caso, donde las vistas no tienen la misma altura, llamar a scrollToPosition en LayoutManager funcionó para desplazarse realmente hacia la parte inferior y ver completamente el último elemento:

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

2

Esto funciona perfectamente bien para mí:

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

2

Desplácese por primera vez cuando ingrese en la vista de reciclador por primera vez y luego use

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

poner menos para desplazarse hacia abajo para desplazarse hacia arriba poner en valor positivo);

si la vista es muy grande en altura, entonces se usa desplazamiento a la posición de desplazamiento particular para la parte superior de la vista y luego se usa

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

0

Solo la respuesta de Ian fue capaz de hacer que mi RecyclerViewpergamino llegara a una posición específica. Sin embargo, The RecyclerViewno pudo desplazarse después cuando lo usé scrollToPosition(). smoothScrollToPosition()funcionó pero la animación inicial lo hizo demasiado lento cuando la lista era larga. La razón fue que el oyente no fue eliminado. Utilicé el siguiente código para eliminar la corriente ViewTreeObservery funcionó de maravilla.

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

0

este código te dará la última publicación primero, creo que esta respuesta es útil.

    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

Probé un método de @galex , funcionó hasta refactorizar. Así que usé una respuesta de @yanchenko y cambié un poco. Probablemente esto se onCreateView()deba a que llamé a desplazamiento desde donde se creó una vista de fragmento (y probablemente no tenía el tamaño correcto).

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

También puede agregar una marca de me viewTreeObserver.isAlivegusta en https://stackoverflow.com/a/39001731/2914140 .


0

Si ninguno de estos funcionó,

deberías intentar usar:

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

Al hacer esto, está solicitando que se muestre un cierto RestraintLayout (o lo que sea que tenga). El pergamino es instantáneo.

Trabajo incluso con el teclado que se muestra.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.