RecyclerView en altına nasıl kaydırılır? scrollToPosition çalışmıyor


161

Etkinliği yükledikten sonra RecyclerView listesinin en altına gitmek istiyorum.

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'da desteklenmeme konusunda bir istisna atar ve conversationView.scrollToPosition(...)hiçbir şey yapmaz gibi görünür .

Yukarıdaki kod bloğundan sonra,

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

ki bu işe yaramıyor. 'De 30 element var GENERIC_MESSAGE_LIST.


Aradığınız scrollToPositionadaptörü ayarı hemen sonra?
ianhanniballake

@ianhanniballake evet, hemen sonra conversationView.setAdapter(conversationViewAdapter);.
waylaidwanderer

4
Muhtemelen -1 yerine +1 çağırıyorsunuz, bu nedenle 5. öğe 0 konumunda başlayacağı için konum [4] olacaktır. +1 yapmak size ArrayOutOfBounds verecekti ... orada çökmedi mi?
RCB

1
Setadapter mRecyclerView.scrollToPosition (carsList.size () - 1) sonrasında ekleyin;
Webserveis

Yanıtlar:


195

Sadece set setStackFromEnd=trueveya setReverseLayout=trueöylesine LLM ucundan öğeleri düzeni edeceğini.

Bu ikisi arasındaki fark setStackFromEnd, görünümü son öğeyi gösterecek şekilde ayarlayacak, düzen yönü aynı kalacaktır. (Bu nedenle, soldan sağa yatay Geri Dönüşümcü Görünümü'nde son öğe gösterilir ve sola kaydırıldığında önceki öğeler gösterilir)

Oysa setReverseLayoutAdaptör tarafından eklenen elemanların sırasını değiştirecektir. Düzen, bir LTR Recycler View'da en solda olacak olan son öğeden başlar ve sağa kaydırıldığında önceki öğeler gösterilir.

Örneklem:

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

Ayrıntılar için belgelere bakın.


133
Bu kullanışlı bir şey olabilir, ancak sorulan soruya cevap vermiyor gibi görünüyor.
joseph

6
Her iki stackFromEnd = true ve setReverseLayout = true değerini ayarlarsanız çalışmaz. Herhangi bir çözüm bilen var mı?
Luccas Correa

10
Sohbet görünümü için linearLayoutManager.setStackFromEnd (true) çalışır.
Muneeb Mirza

7
Bu soruya hiç değinmiyor.
Yörünge

1
Yalnızca görünüm dolana kadar çalışır. Kaydırma niyetinde değil, sadece alttan istifleyin.
MiguelSlv

377

Cevabı bulmak için bu yazıya bakıyordum ama ... Sanırım bu yazıdaki herkes benimle aynı senaryo ile karşı karşıyaydı: scrollToPosition()açık bir nedenden dolayı tamamen göz ardı edildi.

Ne kullanıyordum?

recyclerView.scrollToPosition(items.size());

... ne işe yaradı ?

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

6
recyclerView.scrollToPosition(messages.size()-1);gerçekten benim için çalışıyor, sadece sebebini merak ediyorum.
Motor Bai

25
Bu benim için çalıştı. Java listeleri sıfır tabanlı olduğu için aslında mantıklı. Örneğin, liste [0,1,2] 'nin boyutu 3'tür, ancak son konum 2'dir, çünkü 0 ile başlar.
Calvin

19
FYI, düzgün bir kaydırma için var smoothScrollToPosition(...).
Ivan Morgillo

5
@Ivan Morgilo smoothScrollToPosition hiç çalışmıyor: S
cesards

2
@IvanMorgillo - Cevabınız hem istenen sonuçla hem de daha düzgün = D ile daha iyi çalışıyor gibi görünüyor. konuma kaydırmak benim için biraz buggy gibi görünüyor (sanırım denilen işlev ile görünümün oluşturulması arasında küçük bir gecikme var mı? neyse düzgün çalışmıyor)
Roee

54

Buraya cevap vermek için geç olduğunu biliyorum, yine de çözüm bulmak isteyen varsa aşağıda

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

8
Olması gereken conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);listelerde pozisyonları sıfır tabanlı olarak.
Berťák

2
Son satırın yüksekliği ekran yüksekliğinden daha fazla ise bu yardımcı olmaz.
Anuj Jindal

Buna ihtiyacım vardı. Her şeyin doğru çalışmasını sağlamak için bir işleyici postdelay'e sarmak zorunda kalmama rağmen.
Marc Alexander

18

Geri dönüşüm görünümünde herhangi bir konumdan en alta kaydırmak için

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

Bu kodu mesaj gönderdikten sonra ve sunucudan mesaj almadan önce ekleyin

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

10

Aradığınızda setAdapter, bu hemen düzenlenmez ve öğeleri ekrana yerleştirir (tek bir düzen geçişi alır), bu nedenle scrollToPosition()aramanızın, aradığınızda kaydırılacak gerçek bir öğesi yoktur.

Bunun yerine, kayıt olmalıdır ViewTreeObserver.OnGlobalLayoutListener (via addOnGlobalLayoutListner () bir gelen ViewTreeObserveryarattığı conversationView.getViewTreeObserver()Delays olan) scrollToPosition()ilk düzen geçişte sonrasına kadar:

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
Teşekkürler, ama bu işe yaramıyor. Fırlatmanın çalışmasını durdurarak kaydırmayı keser ve sürüklerseniz, garip bir şekilde hızlı kaydırır.
waylaidwanderer

Dinleyiciyi kaldırmıyormuşsunuz gibi geliyor. Kaydırma sırasında hiç değil, yalnızca bir kez (elemanlar ilk yerleştirildikten hemen sonra) çağrılmalıdır.
ianhanniballake

OnGlobalLayoutListenerMuhtemelen kaldırılamıyor çünkü kontrol ediyorsunuz vto.isAlive(). Nedenini bilmiyorum, ama ViewTreeObserverbazen için değiştirilir Viewböylece yerine denetlenmesine yönelik, isAlivedaha iyi sadece şimdiki olsun sana ViewTreeObserverileconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Dmitry Zaytsev

@ianhanniballake Bu sorunu düzeltmek için bir düzenleme önerdim.
Saket

7

Kotlin için çözüm :

"recyclerView.adapter" ayarladıktan sonra veya "recyclerView.adapter.notifyDataSetChanged ()" sonrasında aşağıdaki kodu uygulayın

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

5

Kotlin'de:

recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }

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

Hah, teşekkürler GlobalLayoutListener! Yeniden düzenleme işleminden sonra kaydırma yönteminizi kullanmak zorunda kaldım. Stackoverflow.com/questions/28264139/… 'de olduğu gibi dinleyiciyi kaldırmayı unutmayın , yoksa RecyclerView öğesini tekrar başa kaydırmayacaksınız.
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));
    }
  }
}

ve

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

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

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

Yukarıdaki kod çalışıyor, ancak pürüzsüz ve serin değil.


4

RecyclerView ile bağlanmış adaptörünüz varsa, bunu yapın.

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


4

Roc cevabı çok yardımcı oluyor. Ben küçük bir blok eklemek istiyorum:

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

4

Görünümlerin aynı yüksekliğe sahip olmadığı durumumda , LayoutManager'da scrollToPosition çağrısı gerçekten en alta kaydırmak ve son öğeyi tamamen görmek için çalıştı:

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

3

Geri dönüşümcü görünümüne ilk kez girerken ilk kez kaydırma yapın ve ardından

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

yukarı kaydırma için eksi olarak aşağı kaydırma için pozitif değer koyun);

görünüm çok büyükse, görünümün üst kısmı için kaydırma konumu belirli bir ofset kullanılırsa

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

2

Bu benim için mükemmel çalışıyor:

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

0

Sadece Ian'ın cevabı RecyclerViewbelirtilen pozisyona ilerlememi başardı . Ancak, RecyclerViewben kullandıktan sonra ilerlemek mümkün değildi scrollToPosition(). smoothScrollToPosition()çalıştı, ancak liste uzun olduğunda ilk animasyon çok yavaşladı. Nedeni dinleyicinin kaldırılmamasıydı. Akımı kaldırmak için aşağıdaki kodu kullandım ve ViewTreeObserverbir cazibe olarak çalıştı.

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

0

Bu kod ilk önce en son gönderi verecektir, bu cevabın yararlı olduğunu düşünüyorum.

    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 yöntemini denedi , yeniden düzenleme yapılana kadar çalıştı. Bu yüzden @yanchenko'nun yanıtını kullandım ve biraz değiştim. Muhtemelen bunun nedeni onCreateView(), bir fragman görünümünün oluşturulduğu (ve muhtemelen doğru boyuta sahip olmadığı) kaydırma işlevini çağırdım .

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

Https://stackoverflow.com/a/39001731/2914140 adresineviewTreeObserver.isAlive benzer bir kontrol de ekleyebilirsiniz .


0

Bunların hiçbiri işe yaramazsa,

kullanmaya çalışmalısınız:

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

Bunu yaparak, belirli bir ConstraintLayout öğesinin (veya sahip olduğunuz her şeyin) görüntülenmesini istersiniz. Kaydırma anında gerçekleşir.

Gösterilen klavye ile bile çalışıyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.