Як прокрутити до низу 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

@ianhanniballake так, це відразу після conversationView.setAdapter(conversationViewAdapter);.
Waylaidwanderer

4
Це, мабуть, тому, що ви викликаєте +1 замість -1, тому 5-й елемент буде позицією [4], тому що він починається з 0. Виконуючи +1, ви отримаєте ArrayOutOfBounds ... чи він не вийшов з ладу?
RCB

1
Додати після setadapter mRecyclerView.scrollToPosition (carsList.size () - 1);
Webserveis

Відповіді:


195

Просто встановіть setStackFromEnd=trueабо setReverseLayout=trueтак , що LLM буде макет елементів з кінця.

Різниця між цими двома полягає в тому setStackFromEnd, що встановить подання відображення останнього елемента, напрямок компонування залишиться колишнім. (Отже, у горизонтальному поданні ліворуч-направо Recycler буде показаний останній елемент, а прокрутка вліво покаже попередні елементи)

Тоді як setReverseLayoutбуде змінено порядок елементів, доданих Адаптером. Макет розпочнеться з останнього елемента, який буде самим лівим у перегляді LTR Recycler View, а потім, прокрутка праворуч покаже попередні елементи.

Зразок:

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

Детальну інформацію див. У документації .


133
Це може бути чимось корисним, але це, здається, не відповідає на поставлене запитання.
Іосіф

6
Якщо встановити і stackFromEnd = true, і setReverseLayout = true, він не працює. Хтось знає якесь вирішення?
Luccas Correa

10
Для перегляду чату працює linearLayoutManager.setStackFromEnd (правда).
Muneeb Mirza

7
Це взагалі не стосується питання.
Орбіта

1
Працює лише до повного перегляду. Він не збирається прокручувати, просто стек знизу.
MiguelSlv

377

Я дивився на цю публікацію, щоб знайти відповідь, але ... Я думаю, що всі на цій посаді зіткнулися з тим же сценарієм, що і я: 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
FYI, для плавного прокручування є smoothScrollToPosition(...).
Іван Моргільо

5
@Ivan Morgilo smoothScrollToPosition взагалі не працює: S
cesards

2
@IvanMorgillo - ваша відповідь, здається, працює краще як із бажаним результатом, так і більш гладким = D. здається, що прокрутка до позиції для мене трохи баггі (я здогадуюсь, що між функцією, що називається, і створенням перегляду невелика затримка? все одно вона не працює належним чином)
Roee

54

Я знаю, що тут пізно відповісти, все ж якщо хтось хоче знати рішення - нижче

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

8
Це має бути так, conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);як позиції в списках засновані на нулі.
Berťák

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

1
Дякую, але це не працює. Він перериває прокрутку, зупиняючи перекидатись на роботу, а якщо перетягнути, вона прокручується дивно швидко.
waylaidwanderer

Здається, ви не видаляєте слухача. Його слід викликати лише один раз (відразу після того, як елементи вперше викладені), і зовсім не під час прокрутки.
ianhanniballake

OnGlobalLayoutListenerймовірно, не видаляється, тому що ви перевіряєте vto.isAlive(). Я не знаю причину, але ViewTreeObserverіноді змінюється на View, тому замість того, щоб перевіряти для isAliveвас, краще просто ознайомитися ViewTreeObserverзconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Дмитро Зайцев

@ianhanniballake Я запропонував змінити, щоб виправити цю проблему.
Сакет

7

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

застосувати код нижче після налаштування "recilerView.adapter" або після "recilerView.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

Якщо у вас є доданий адаптер з recilerView, тоді просто зробіть це.

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


4

Відповідь Рока - це чудова допомога. Я хотів би додати до нього невеликий блок:

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

4

У моєму випадку, коли представлення не мають однакової висоти, виклик scrollToPosition у LayoutManager працював, щоб дійсно прокрутити донизу і повністю переглянути останній елемент:

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

3

Першу прокрутку при першому введенні в режим перегляду рециркулятора, а потім використовувати

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

поставити мінус для прокрутки вниз, для прокрутки вгору поставити додатне значення);

якщо перегляд дуже великий у висоту, тоді для прокрутки вгорі використовується прокруткаположення, яке певне зміщення використовується тоді

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 .


0

Якщо жодне з них не спрацювало,

ви повинні спробувати використовувати:

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

Роблячи це, ви вимагаєте відобразити певний ConstraintLayout (або все, що у вас є). Прокрутка миттєва.

Я працюю навіть із показаною клавіатурою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.