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에서 지원되지 않는 것에 대한 예외를 throw하고 conversationView.scrollToPosition(...)아무것도하지 않는 것 같습니다.

위의 코드 블록 뒤에 추가했습니다

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

작동하지 않습니다. 에 30 개의 요소가 GENERIC_MESSAGE_LIST있습니다.


scrollToPosition어댑터를 설정 한 직후에 전화 하십니까?
ianhanniballake

@ianhanniballake 네, 바로 다음 conversationView.setAdapter(conversationViewAdapter);입니다.
waylaidwanderer

4
아마도 -1 대신 +1을 호출하기 때문에 5 번째 요소는 0에서 시작하기 때문에 위치 [4]가됩니다. +1을 수행하면 ArrayOutOfBounds가 발생합니다. 거기서 충돌하지 않았습니까?
RCB

1
setadapter 후 추가 mRecyclerView.scrollToPosition (carsList.size ()-1);
Webserveis

답변:


195

그냥 설정 setStackFromEnd=true또는 setReverseLayout=true너무 LLM은 끝에서 항목을 레이아웃 것이다.

이 두 가지의 차이점 setStackFromEnd은 마지막 요소를 표시하도록보기를 설정하고 레이아웃 방향이 동일하게 유지된다는 것입니다. (왼쪽에서 오른쪽으로 가로 리사이클 러보기에서 마지막 요소가 표시되고 왼쪽으로 스크롤하면 이전 요소가 표시됩니다)

반면 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 (true) 작동합니다.
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부터 시작하기 때문에 실제로 의미가 있습니다. 예를 들어리스트 [0,1,2]의 크기는 3이지만 마지막 위치는 0으로 시작하기 때문에 2입니다.
Calvin

19
참고로 부드러운 스크롤에는 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**);목록의 위치가 0부터 시작 해야합니다 .
Berťák

2
마지막 행의 높이가 화면 높이보다 높으면 도움이되지 않습니다.
Anuj Jindal

이것이 내가 필요한 것입니다. 모든 것을 올바르게 작동 시키려면 처리기 postdelay에 포장해야했습니다.
Marc Alexander

18

recyclerview의 임의의 위치에서 아래로 스크롤하려면

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 () 를 통해) 등록해야 첫 번째 레이아웃 통과 후까지 지연됩니다 .ViewTreeObserverconversationView.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
Dmitry Zaytsev

@ianhanniballake이 문제를 해결하기 위해 수정을 제안했습니다.
Saket

7

코 틀린 솔루션 :

"recyclerView.adapter"를 설정 한 후 또는 "recyclerView.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

recyclerView에 어댑터가 연결되어 있으면이 작업을 수행하십시오.

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


4

Roc 답변은 큰 도움이됩니다. 작은 블록을 추가하고 싶습니다.

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

4

뷰의 높이가 동일하지 않은 경우 LayoutManager 에서 scrollToPosition을 호출 하면 실제로 맨 아래로 스크롤하여 마지막 항목을 완전히 볼 수 있습니다.

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

Ian의 답변 만 RecyclerView스크롤을 지정된 위치 로 만들 수있었습니다 . 그러나를 RecyclerView사용할 때 The 은 (는) 나중에 스크롤 할 수 없습니다 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 의 답변을 사용 하고 조금 변경했습니다. 아마도 이것은 from from scrolling을 호출했기 때문에 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)
            }
        }
    })
}

https://stackoverflow.com/a/39001731/2914140viewTreeObserver.isAlive 에서 확인 표시를 추가 할 수도 있습니다 .


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.