วิธีเลื่อนไปที่ด้านล่างของ 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 จากนั้นการเลื่อนไปทางขวาจะแสดงองค์ประกอบก่อนหน้า

ตัวอย่าง:

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

ดูเอกสารประกอบสำหรับรายละเอียด


133
นี่อาจเป็นสิ่งที่ผู้ใช้งาน แต่ดูเหมือนจะไม่ตอบคำถามที่ถูกถาม
joseph

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);ได้ผลจริงสำหรับฉันฉันแค่สงสัยถึงเหตุผล
Engine Bai

25
สิ่งนี้ใช้ได้สำหรับฉัน มันสมเหตุสมผลแล้วเนื่องจากรายการ Java นั้นเป็นศูนย์ รายการเช่น [0,1,2] มีขนาด 3 แต่ตำแหน่งสุดท้ายคือ 2 เพราะมันเริ่มต้นด้วย 0
Calvin

19
FYI smoothScrollToPosition(...)สำหรับเลื่อนเรียบมี
Ivan Morgillo

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
สิ่งนี้จะไม่ช่วยถ้าความสูงของแถวสุดท้ายมากกว่าความสูงของหน้าจอ
Anuj Jindal

นี่คือสิ่งที่ฉันต้องการ แม้ว่าฉันจะต้องห่อไว้ในตัวจัดการโพสต์เดย์เพื่อให้ทุกอย่างทำงานได้อย่างถูกต้อง
Marc Alexander

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

OnGlobalLayoutListenervto.isAlive()อาจจะไม่ได้รับการถอดออกเพราะคุณกำลังตรวจสอบ ฉันไม่ทราบเหตุผล แต่ViewTreeObserverบางครั้งก็เปลี่ยนเป็น a Viewดังนั้นแทนที่จะตรวจสอบให้isAliveคุณดีกว่าเพิ่งรับปัจจุบันViewTreeObserverด้วยconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Dmitry Zaytsev

@ianhanniballake ฉันได้เสนอการแก้ไขเพื่อแก้ไขปัญหานี้
Saket

7

โซลูชันสำหรับKotlin :

ใช้รหัสด้านล่างหลังจากตั้งค่า "recyclerView.adapter" หรือหลัง "recyclerView.adapter.notifyDataSetChanged ()"

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

5

ใน Kotlin:

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

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 บางตัว (หรืออะไรก็ตามที่คุณมี) ที่จะแสดง เลื่อนเป็นทันที

ฉันใช้งานได้แม้กับแป้นพิมพ์ที่แสดง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.