Как прокрутить до конца 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.

waylaidwanderer
источник
Вы звоните scrollToPositionсразу после установки адаптера?
ianhanniballake
@ianhanniballake да, это сразу после conversationView.setAdapter(conversationViewAdapter);.
waylaidwanderer
4
Вероятно, это потому, что вы звоните +1 вместо -1, поэтому 5-й элемент будет позицией [4], потому что он начинается с 0. Выполнение +1 даст вам ArrayOutOfBounds ... разве там не произошел сбой?
RCB
1
Добавить после setadapter mRecyclerView.scrollToPosition (carsList.size () - 1);
Вебсервис

Ответы:

196

Просто установите setStackFromEnd=trueили setReverseLayout=trueтак, чтобы LLM размещал элементы с конца.

Разница между этими двумя заключается в том, что setStackFromEndв представлении будет отображаться последний элемент, направление макета останется прежним. (Таким образом, в горизонтальном представлении Recycler слева направо будет показан последний элемент, а при прокрутке влево будут отображены более ранние элементы)

Принимая во внимание, setReverseLayoutчто изменится порядок элементов, добавленных адаптером. Компоновка начнется с последнего элемента, который будет самым левым в представлении LTR Recycler, а затем при прокрутке вправо отобразятся более ранние элементы.

Образец:

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

Смотрите документацию для деталей.

Yigit
источник
133
Это может быть что-то полезное, но, похоже, оно не отвечает на заданный вопрос.
Джозеф
6
Если вы установите оба stackFromEnd = true и setReverseLayout = true, это не сработает. Кто-нибудь знает обходной путь?
Лукас Корреа
10
Для просмотра чата работает linearLayoutManager.setStackFromEnd (true).
Муниб Мирза
7
Это не решает вопрос вообще.
Орбита
1
Работает только до полного просмотра. Он не предназначен для прокрутки, просто складывается снизу.
MiguelSlv
379

Я искал этот пост, чтобы найти ответ, но ... я думаю, что все в этом посте столкнулись с тем же сценарием, что и я: scrollToPosition()был полностью проигнорирован по очевидной причине.

Что я использовал?

recyclerView.scrollToPosition(items.size());

... что РАБОТАЕТ ?

recyclerView.scrollToPosition(items.size() - 1);
Рок Боронат
источник
6
recyclerView.scrollToPosition(messages.size()-1);действительно работает для меня, мне просто интересно, причина.
Двигатель Бай
25
Это сработало для меня. Это действительно имеет смысл, потому что списки Java основаны на нуле. Например, список [0,1,2] имеет размер 3, но последняя позиция равна 2, потому что он начинается с 0.
Calvin
19
К вашему сведению, для плавной прокрутки, есть smoothScrollToPosition(...).
Иван Моргильо
5
@ Иван Моргило smoothScrollToPosition вообще не работает: S
открытки
2
@IvanMorgillo - кажется, ваш ответ работает лучше как с желаемым результатом, так и более плавно = D. кажется, что прокрутка до позиции немного ошибочна для меня (я предполагаю, что есть небольшая задержка между вызываемой функцией и созданием представления? в любом случае это не работает должным образом)
Роу
54

Я знаю, что уже поздно, чтобы ответить здесь, но если кто-то хочет знать решение ниже

conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() - 1);
WritingForAnroid
источник
8
Так должно быть, conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);поскольку позиции в списках начинаются с нуля.
Берняк
2
Это не поможет, если высота последнего ряда больше, чем высота экрана.
Анудж Джиндал
Это то, что мне было нужно. Хотя мне пришлось обернуть его в пост-задержку обработчика, чтобы все работало правильно.
Марк Александр
18

Для прокрутки вниз из любой позиции в обзоре переработчика вниз

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 () от ViewTreeObserverсозданного conversationView.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.
  }
});
ianhanniballake
источник
1
Спасибо, но это не работает. Он прерывает прокрутку, останавливая бросание с работы, и если вы перетаскиваете, он прокручивается странно быстро.
waylaidwanderer
Похоже, вы не удаляете слушателя. Его следует вызывать только один раз (сразу после первого размещения элементов), а не во время прокрутки.
ianhanniballake
OnGlobalLayoutListenerвероятно, не удаляется, потому что вы проверяете vto.isAlive(). Я не знаю причину, но ViewTreeObserverиногда меняется для View, так что вместо проверки isAliveвам лучше просто получить ток ViewTreeObserverсconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Дмитрий Зайцев
@ianhanniballake Я предложил изменить, чтобы исправить эту проблему.
Сакет
7

Решение для Котлина :

применять приведенный ниже код после настройки «recyclerView.adapter» или после «recyclerView.adapter.notifyDataSetChanged ()»

recyclerView.scrollToPosition(recyclerView.adapter.itemCount - 1)
AbhinayMe
источник
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);
abedfar
источник
4

В моем случае, когда представления не имеют одинаковую высоту, вызов scrollToPosition в LayoutManager работал для того, чтобы действительно прокрутить до конца и полностью увидеть последний элемент:

recycler.getLayoutManager().scrollToPosition(adapter.getItemCount() - 1);
Galex
источник
3

Первая прокрутка при первом входе в режим рециркуляции, затем используйте

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

положить в минус для прокрутки вниз для прокрутки вверх (положительное значение);

если вид очень большой по высоте, то для верхней части вида используется конкретное смещение scrolltoposition, тогда вы используете

int overallXScroldl =chatMessageBinding.rvChat.computeVerticalScrollOffset();
chatMessageBinding.rvChat.smoothScrollBy(0, Math.abs(overallXScroldl));
Дишант Каватра
источник
2

Это прекрасно работает для меня:

AdapterChart adapterChart = new AdapterChart(getContext(),messageList);
recyclerView.setAdapter(adapterChart);
recyclerView.scrollToPosition(recyclerView.getAdapter().getItemCount()-1);
амареш наемник
источник
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)
            }
        }
    })
}

Вы также можете добавить проверку viewTreeObserver.isAliveкак в https://stackoverflow.com/a/39001731/2914140 .

CoolMind
источник
0

Если ничего из этого не сработало,

Вы должны попытаться использовать:

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

Делая это, вы запрашиваете определенный ConstraintLayout (или все, что у вас есть) для отображения. Свиток мгновенный.

Я работаю даже с показанной клавиатурой.

Arnaud
источник