Bagaimana cara menggulir ke bagian bawah RecyclerView? scrollToPosition tidak berfungsi

161

Saya ingin menggulir ke bagian bawah daftar RecyclerView setelah memuat aktivitas.

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(...)melempar pengecualian tentang tidak didukung di RecyclerView, dan conversationView.scrollToPosition(...)sepertinya tidak melakukan apa-apa.

Setelah blok kode di atas, saya menambahkan

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

yang tidak bekerja Ada 30 elemen dalam GENERIC_MESSAGE_LIST.

waylaidwanderer
sumber
Apakah Anda menelepon scrollToPositionsegera setelah mengatur adaptor?
ianhanniballake
@ianhanniballake ya, tepat setelah itu conversationView.setAdapter(conversationViewAdapter);.
waylaidwanderer
4
Mungkin karena Anda memanggil +1 dan bukan -1 sehingga elemen ke-5 adalah posisi [4] karena dimulai dari 0. Melakukan +1 akan memberi Anda ArrayOutOfBounds ... apakah tidak mogok di sana?
RCB
1
Tambahkan setelah setadapter mRecyclerView.scrollToPosition (carsList.size () - 1);
Webserveis

Jawaban:

194

Cukup atur setStackFromEnd=trueatau setReverseLayout=truejadi LLM akan menata item dari ujung.

Perbedaan antara keduanya adalah bahwa setStackFromEndakan mengatur tampilan untuk menunjukkan elemen terakhir, arah tata letak akan tetap sama. (Jadi, dalam Tampilan Recycler horizontal kiri-ke-kanan, elemen terakhir akan ditampilkan dan menggulir ke kiri akan menampilkan elemen sebelumnya)

Sedangkan setReverseLayoutakan mengubah urutan elemen yang ditambahkan oleh Adaptor. Tata letak akan dimulai dari elemen terakhir, yang akan menjadi paling kiri di LTR Recycler View dan kemudian, gulir ke kanan akan menampilkan elemen sebelumnya.

Sampel:

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

Lihat dokumentasi untuk detailnya.

yigit
sumber
132
Ini mungkin sesuatu yang bermanfaat, tetapi sepertinya tidak menjawab pertanyaan yang diajukan.
joseph
6
Jika Anda mengatur kedua stackFromEnd = true dan setReverseLayout = true itu tidak berfungsi. Apakah ada yang tahu solusinya?
Luccas Correa
10
Untuk tampilan obrolan linearLayoutManager.setStackFromEnd (true) berfungsi.
Muneeb Mirza
7
Ini sama sekali tidak menjawab pertanyaan.
Orbit
1
Hanya berfungsi sampai tampilan penuh. Itu tidak bermaksud gulir, cukup tumpuk dari bawah.
MiguelSlv
376

Saya melihat posting ini untuk menemukan jawabannya tetapi ... Saya pikir semua orang di posting ini menghadapi skenario yang sama dengan saya: scrollToPosition()sepenuhnya diabaikan, karena alasan yang jelas.

Apa yang saya gunakan?

recyclerView.scrollToPosition(items.size());

... apa yang BEKERJA ?

recyclerView.scrollToPosition(items.size() - 1);
Roc Boronat
sumber
6
recyclerView.scrollToPosition(messages.size()-1);benar-benar bekerja untuk saya, saya hanya ingin tahu alasannya.
Engine Bai
25
Ini berhasil untuk saya. Ini sebenarnya masuk akal karena daftar Java berbasis nol. Misalnya daftar [0,1,2] memiliki ukuran 3 tetapi posisi terakhir adalah 2 karena dimulai dengan 0.
Calvin
19
FYI, untuk scroll yang halus, ada smoothScrollToPosition(...).
Ivan Morgillo
5
@Ivan Morgilo smoothScrollToPosition tidak bekerja sama sekali: S
cesards
2
@IvanMorgillo - jawaban Anda tampaknya berfungsi lebih baik baik dengan hasil yang diinginkan dan lebih halus = D. Sepertinya scroll to position agak buggy buat saya (tebakan saya ada sedikit penundaan antara fungsi yang dipanggil dan pembuatan view? lagipula itu tidak berfungsi dengan baik)
Roee
54

Saya tahu sudah terlambat untuk menjawab di sini, masih ada yang mau tahu solusinya ada di bawah ini

conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() - 1);
WritingForAnroid
sumber
8
Itu harus conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);sebagai posisi dalam daftar berbasis nol.
Berťák
2
Ini tidak akan membantu jika ketinggian baris terakhir lebih dari tinggi layar.
Anuj Jindal
Inilah yang saya butuhkan. Meskipun saya harus membungkusnya dalam postler handler pawang untuk mendapatkan semuanya berfungsi dengan benar.
Marc Alexander
18

Untuk scrolldown dari posisi apa pun di recyclerview ke bawah

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
sumber
15

Tambahkan kode ini setelah mengirim pesan dan sebelum menerima pesan dari server

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

Saat Anda menelepon setAdapter, itu tidak langsung menampilkan dan memposisikan item di layar (yang memerlukan satu tata letak lulus) maka scrollToPosition()panggilan Anda tidak memiliki elemen aktual untuk menggulir ke saat Anda memanggilnya.

Sebagai gantinya, Anda harus mendaftarkan ViewTreeObserver.OnGlobalLayoutListener (via addOnGlobalLayoutListner () dari yang ViewTreeObserverdibuat oleh conversationView.getViewTreeObserver()) yang menunda Anda scrollToPosition()hingga setelah tata letak pertama berlalu:

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
sumber
1
Terima kasih, tetapi ini tidak berhasil. Ini merusak gulir dengan menghentikan gulir dari bekerja, dan jika Anda seret, gulir dengan aneh cepat.
waylaidwanderer
Sepertinya Anda tidak menghapus pendengar. Seharusnya hanya dipanggil tepat sekali (tepat setelah elemen pertama kali diletakkan) dan tidak sama sekali selama pengguliran.
ianhanniballake
OnGlobalLayoutListenermungkin tidak dihapus karena Anda memeriksa vto.isAlive(). Saya tidak tahu alasannya, tetapi ViewTreeObserverkadang-kadang diubah untuk a View, jadi alih-alih memeriksa isAliveAnda lebih baik, dapatkan saja ViewTreeObserverdenganconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Dmitry Zaytsev
@ianhanniballake Saya sudah mengusulkan edit untuk memperbaiki masalah ini.
Saket
6

Solusi untuk Kotlin :

terapkan kode di bawah ini setelah menetapkan "recyclerView.adapter" atau setelah "recyclerView.adapter.notifyDataSetChanged ()"

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

Di Kotlin:

recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }

private fun scrollToEnd() =
        (adapter.itemCount - 1).takeIf { it > 0 }?.let(recyclerView::smoothScrollToPosition)
yanchenko
sumber
Hah, terima kasih untuk GlobalLayoutListener! Setelah refactoring saya harus menggunakan metode Anda untuk menggulir. Jangan lupa untuk menghapus pendengar seperti di stackoverflow.com/questions/28264139/… , atau Anda tidak akan menggulir RecyclerView kembali ke awal.
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));
    }
  }
}

dan

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

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

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

kode di atas berfungsi, tetapi tidak mulus dan tidak keren.

Yuki Yoshida
sumber
4

JIKA Anda memiliki adaptor yang terpasang dengan recyclerView maka lakukan saja ini.

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

Wasim Amin
sumber
4

Jawaban roc sangat membantu. Saya ingin menambahkan blok kecil ke sana:

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

Dalam kasus saya di mana tampilan tidak memiliki ketinggian yang sama, memanggil scrollToPosition pada LayoutManager berfungsi untuk benar-benar menggulir ke bawah dan melihat sepenuhnya item terakhir:

recycler.getLayoutManager().scrollToPosition(adapter.getItemCount() - 1);
galex
sumber
2

Ini berfungsi dengan baik bagi saya:

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

Gulir pertama kali saat masuk dalam tampilan pendaur ulang pertama kali kemudian gunakan

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

masukkan minus untuk gulir ke bawah untuk gulir ke atas, masukkan nilai positif);

jika tampilan sangat tinggi maka scrolltoposition offset tertentu digunakan untuk bagian atas tampilan maka Anda gunakan

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

Hanya jawaban Ian yang dapat membuat RecyclerViewgulungan saya ke posisi yang ditentukan. Namun, The RecyclerViewtidak dapat menggulir setelah itu ketika saya menggunakan scrollToPosition(). smoothScrollToPosition()berfungsi tetapi animasi awal membuatnya terlalu lambat ketika daftar panjang. Alasannya adalah pendengarnya tidak dihapus. Saya menggunakan kode di bawah ini untuk menghapus arus ViewTreeObserverdan itu berfungsi sebagai pesona.

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

kode ini akan memberi Anda posting terbaru terlebih dahulu, saya pikir jawaban ini sangat membantu.

    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
sumber
0

Mencoba metode @galex , itu berhasil hingga refactoring. Jadi saya menggunakan jawaban @ yanchenko dan berubah sedikit. Mungkin ini karena saya menelepon gulir dari onCreateView(), tempat tampilan fragmen dibuat (dan mungkin tidak memiliki ukuran yang tepat).

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

Anda juga dapat menambahkan tanda centang viewTreeObserver.isAlivedi https://stackoverflow.com/a/39001731/2914140 .

CoolMind
sumber
0

Jika tidak ada yang berhasil,

Anda harus mencoba menggunakan:

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

Dengan melakukan ini, Anda meminta ConstraintLayout tertentu (Atau apa pun yang Anda miliki) untuk ditampilkan. Gulir itu instan.

Saya bekerja bahkan dengan keyboard yang ditampilkan.

Arnaud
sumber