Làm cách nào để cuộn xuống dưới cùng của RecyclerView? scrollToPocation không hoạt động


161

Tôi muốn cuộn xuống cuối danh sách RecyclerView sau khi tải hoạt động.

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(...)đưa ra một ngoại lệ về việc không được hỗ trợ trong RecyclerView và conversationView.scrollToPosition(...)dường như không làm gì cả.

Sau khối mã trên, tôi đã thêm

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

mà không làm việc Có 30 yếu tố trong GENERIC_MESSAGE_LIST.


Bạn có gọi scrollToPositionngay sau khi cài đặt bộ chuyển đổi không?
ianhanniballake

@ianhanniballake có, nó ngay sau đó conversationView.setAdapter(conversationViewAdapter);.
waylaidwanderer

4
Có thể là do bạn đang gọi +1 thay vì -1 nên phần tử thứ 5 sẽ là vị trí [4] bởi vì nó bắt đầu từ 0. Làm +1 sẽ mang lại cho bạn một ArrayOutOfBound ... nó không bị sập ở đó phải không?
RCB

1
Thêm sau setad CHƯƠNG mRecyclerView.scrollToP vị trí (CarsList.size () - 1);
Webserveis

Câu trả lời:


194

Chỉ cần đặt setStackFromEnd=truehoặc setReverseLayout=trueđể LLM sẽ bố trí các mục từ cuối.

Sự khác biệt giữa hai điều này là setStackFromEndsẽ đặt chế độ xem hiển thị phần tử cuối cùng, hướng bố cục sẽ giữ nguyên. (Vì vậy, trong Chế độ xem tái chế ngang từ trái sang phải, phần tử cuối cùng sẽ được hiển thị và cuộn sang bên trái sẽ hiển thị các phần tử trước đó)

Trong khi đó setReverseLayoutsẽ thay đổi thứ tự của các yếu tố được thêm bởi Bộ điều hợp. Bố cục sẽ bắt đầu từ phần tử cuối cùng, phần lớn nhất bên trái trong Chế độ xem LTR Recycler và sau đó, cuộn sang phải sẽ hiển thị các phần tử trước đó.

Mẫu vật:

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

Xem tài liệu để biết chi tiết.


132
Đây có thể là một cái gì đó hữu ích, nhưng nó dường như không trả lời câu hỏi đã được hỏi.
joseph

6
Nếu bạn đặt cả stackFromEnd = true và setReverseLayout = true thì nó không hoạt động. Có ai biết cách giải quyết nào không?
Luccas Correa

10
Đối với chế độ xem trò chuyện linearLayoutManager.setStackFromEnd (true) hoạt động.
Muneeb Mirza

7
Điều này không giải quyết câu hỏi nào cả.
Quỹ đạo

1
Chỉ hoạt động cho đến khi xem là đầy đủ. Nó không có ý định cuộn, chỉ xếp chồng từ dưới lên.
MiguelSlv

376

Tôi đã xem bài đăng này để tìm câu trả lời nhưng ... Tôi nghĩ mọi người trong bài đăng này đều phải đối mặt với cùng một kịch bản như tôi: scrollToPosition()đã hoàn toàn bị bỏ qua, vì một lý do rõ ràng.

Những gì tôi đã sử dụng?

recyclerView.scrollToPosition(items.size());

... LÀM VIỆC GÌ ?

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

6
recyclerView.scrollToPosition(messages.size()-1);thực sự làm việc cho tôi, tôi chỉ tự hỏi lý do.
Động cơ Bai

25
Điều này làm việc cho tôi. Nó thực sự có ý nghĩa bởi vì danh sách Java là không dựa trên. Ví dụ: danh sách [0,1,2] có kích thước bằng 3 nhưng vị trí cuối cùng là 2 vì nó bắt đầu bằng 0.
Calvin

19
FYI, cho một cuộn trơn tru, có smoothScrollToPosition(...).
Ivan Morgillo

5
@Ivan Morgilo smoothScrollToPocation hoàn toàn không hoạt động: S
mổ vào

2
@IvanMorgillo - câu trả lời của bạn dường như hoạt động tốt hơn cả với kết quả mong muốn và suôn sẻ hơn = D. Có vẻ như cuộn đến vị trí là một lỗi nhỏ đối với tôi (tôi đoán là có một độ trễ nhỏ giữa chức năng được gọi và việc tạo chế độ xem? dù sao nó không hoạt động đúng)
Roee

54

Tôi biết đã muộn để trả lời ở đây, nếu vẫn có ai muốn biết giải pháp bên dưới

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

8
Nó phải conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);là vị trí trong danh sách là dựa trên không.
Berťák

2
Điều này sẽ không giúp nếu chiều cao của hàng cuối cùng nhiều hơn chiều cao màn hình.
Anuj Jindal

Đây là những gì tôi cần. Mặc dù tôi đã phải bọc nó trong một postdelay xử lý để mọi thứ hoạt động chính xác.
Marc Alexander

18

Để cuộn xuống từ bất kỳ vị trí nào trong recyclerview xuống dưới cùng

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

Thêm mã này sau khi gửi tin nhắn và trước khi nhận tin nhắn từ máy chủ

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

10

Khi bạn gọi setAdapter, điều đó không ngay lập tức bố trí và định vị các mục trên màn hình (chỉ mất một bố cục duy nhất) do đó scrollToPosition()cuộc gọi của bạn không có yếu tố thực tế để cuộn đến khi bạn gọi nó.

Thay vào đó, bạn nên đăng ký ViewTreeObserver.OnGlobalLayoutListener (thông qua addOnGlobalLayoutListner () từ một ViewTreeObserverđược tạo bởi conversationView.getViewTreeObserver()) sẽ trì hoãn scrollToPosition()cho đến khi vượt qua bố cục đầu tiên:

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
Cảm ơn, nhưng điều này không hoạt động. Nó phá vỡ cuộn bằng cách ngừng hoạt động, và nếu bạn kéo, nó sẽ cuộn rất nhanh.
waylaidwanderer

Âm thanh như bạn không loại bỏ người nghe. Nó chỉ nên được gọi chính xác một lần (ngay sau khi các yếu tố được đặt ra đầu tiên) và hoàn toàn không trong quá trình cuộn.
ianhanniballake

OnGlobalLayoutListenercó thể không bị xóa vì bạn đang kiểm tra vto.isAlive(). Tôi không biết lý do, nhưng ViewTreeObserverđôi khi được thay đổi vì một View, vì vậy thay vì kiểm tra cho isAlivebạn tốt hơn chỉ nên cập nhật ViewTreeObservervớiconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
Dmitry Zaytsev

@ianhanniballake Tôi đã đề xuất một chỉnh sửa để khắc phục vấn đề này.
Saket

6

Giải pháp cho Kotlin :

áp dụng mã bên dưới sau khi thiết lập "recyclerView.ad CHƯƠNG" hoặc sau "recyclerView.ad CHƯƠNG.notifyDataSetChanged ()"

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

5

Trong Kotlin:

recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }

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

Hah, cảm ơn vì GlobalLayoutListener! Sau khi tái cấu trúc tôi phải sử dụng phương pháp của bạn để cuộn. Đừng quên xóa người nghe như trong stackoverflow.com/questions/28264139/ , hoặc bạn sẽ không cuộn RecyclerView trở lại từ đầu.
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);

ở trên mã hoạt động, nhưng nó không trơn tru và không mát mẻ.


4

NẾU bạn có bộ chuyển đổi kèm theo recyclerView thì chỉ cần làm điều này.

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


4

Câu trả lời của Roc là một sự trợ giúp tuyệt vời. Tôi muốn thêm một khối nhỏ vào nó:

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

4

Trong trường hợp của tôi khi các chế độ xem không có cùng chiều cao, hãy gọi scrollToPocation trên LayoutManager hoạt động để thực sự cuộn xuống phía dưới và xem đầy đủ mục cuối cùng:

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

2

Điều này hoạt động hoàn toàn tốt cho tôi:

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

2

Lần đầu tiên cuộn khi vào chế độ xem tái chế lần đầu tiên sau đó sử dụng

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

đặt trong dấu trừ để cuộn xuống để cuộn lên đặt giá trị dương);

nếu chế độ xem rất lớn về chiều cao thì phần bù đặc biệt của cuộn được sử dụng cho phần trên của chế độ xem thì bạn sử dụng

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

0

Chỉ có câu trả lời của Ian là có thể khiến RecyclerViewcuộn của tôi đến một vị trí xác định. Tuy nhiên, The RecyclerViewđã không thể cuộn sau khi tôi sử dụng scrollToPosition(). smoothScrollToPosition()hoạt động nhưng hoạt hình ban đầu làm cho nó quá chậm khi danh sách dài. Lý do là người nghe đã không được gỡ bỏ. Tôi đã sử dụng mã dưới đây để loại bỏ hiện tại ViewTreeObservervà nó hoạt động như một nét duyên dáng.

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

0

mã này sẽ cung cấp cho bạn bài mới nhất đầu tiên, tôi nghĩ rằng câu trả lời này là hữu ích.

    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

Đã thử một phương pháp của @galex , nó đã hoạt động cho đến khi tái cấu trúc. Vì vậy, tôi đã sử dụng một câu trả lời của @yanchenko và thay đổi một chút. Có lẽ điều này là do tôi gọi là cuộn từ đó onCreateView(), nơi chế độ xem phân đoạn được tạo (và có lẽ không có kích thước phù hợp).

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

Bạn cũng có thể thêm một kiểm tra viewTreeObserver.isAlivethích trong https://stackoverflow.com/a/39001731/2914140 .


0

Nếu không ai trong số này làm việc,

bạn nên thử sử dụng:

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

Bằng cách này, bạn đang yêu cầu một ConstraintLayout nhất định (Hoặc bất cứ thứ gì bạn có) được hiển thị. Cuộn là ngay lập tức.

Tôi làm việc ngay cả với bàn phím được hiển thị.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.