RecyclerView로 끝없는 목록을 구현하는 방법은 무엇입니까?


346

로 변경 ListView하고 싶습니다 RecyclerView. 내가 사용하고자하는 onScrollOnScrollListener의를 RecyclerView사용자가 목록의 끝으로 스크롤 여부를 결정 할 수 있습니다.

REST 서비스에서 새 데이터를 가져올 수 있도록 사용자가 목록 끝으로 스크롤하는지 어떻게 알 수 있습니까?


이 링크를 확인하십시오. 도움이 될 것입니다 .. stackoverflow.com/questions/26293461/…
samsad

32
질문을주의 깊게 읽으십시오! ListView 로이 작업을 수행하는 방법을 알고 있지만 RecycleView 로 구현하는 방법은 알고 있지 않습니다 .
erdna

android.data에 loadmore onscrolllistener를 구현하는 방법로드되었지만 기존 데이터에서 업데이트하십시오.
Harsha

2
:이 라이브러리를 사용할 수 있습니다 github.com/rockerhieu/rv-adapter-endless을 . 그것은의 아이디어에 기반 cwac-endless을위한 ListView.
Hieu Rocker

답변:


422

@Kushal 덕분에 이것이 구현 한 방법입니다.

private boolean loading = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (dy > 0) { //check for scroll down
            visibleItemCount = mLayoutManager.getChildCount();
            totalItemCount = mLayoutManager.getItemCount();
            pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();

            if (loading) {
                if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) {
                    loading = false;
                    Log.v("...", "Last Item Wow !");
                    // Do pagination.. i.e. fetch new data
                }
            }
        }
    }
});

추가하는 것을 잊지 마십시오

LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);

11
내가 할 수있는 일StaggeredGridLayoutManager
Pratik Butani

16
mLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
laaptu는

45
findFirstVisibleItemPosition()해결되지 않은 모든 사람에게 다음과 같이 변경해야 RecyclerView.LayoutManager합니다.LinearLayoutManager
Amr Mohammed

26
loading = true다시 만드는 조건은 어디에 있습니까?
Bhargav

10
부품 loading = true뒤에 추가하는 것을 잊지 마십시오 //Do pagination. 그렇지 않으면이 코드는 한 번만 실행되며 더 많은 항목을로드 할 수 없습니다.
Shaishav Jogani

165

이 변수들을 만드십시오.

private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;

재활용보기를 위해 스크롤로 설정하십시오.

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        visibleItemCount = mRecyclerView.getChildCount();
        totalItemCount = mLayoutManager.getItemCount();
        firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading && (totalItemCount - visibleItemCount) 
            <= (firstVisibleItem + visibleThreshold)) {
            // End has been reached

            Log.i("Yaeye!", "end called");

            // Do something

            loading = true;
        }
    }
});

참고 :의LinearLayoutManager 레이아웃 관리자로 사용 하고 있는지 확인하십시오 RecyclerView.

LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);

그리고 그리드

GridLayoutManager mLayoutManager;
mLayoutManager = new GridLayoutManager(getActivity(), spanCount);
mRecyclerView.setLayoutManager(mLayoutManager);

끝없는 두루마리로 재미있게 보내십시오! ^. ^

업데이트 : mRecyclerView. setOnScrollListener () 는 더 이상 사용되지 않으며 대체되며 mRecyclerView.addOnScrollListener()경고가 사라집니다! 이 SO 질문 에서 더 많은 것을 읽을 수 있습니다 .

Android가 공식적으로 Kotlin을 지원하므로 여기에 동일한 업데이트가 있습니다.

OnScrollListener 만들기

class OnScrollListener(val layoutManager: LinearLayoutManager, val adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>, val dataList: MutableList<Int>) : RecyclerView.OnScrollListener() {
    var previousTotal = 0
    var loading = true
    val visibleThreshold = 10
    var firstVisibleItem = 0
    var visibleItemCount = 0
    var totalItemCount = 0

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)

        visibleItemCount = recyclerView.childCount
        totalItemCount = layoutManager.itemCount
        firstVisibleItem = layoutManager.findFirstVisibleItemPosition()

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false
                previousTotal = totalItemCount
            }
        }

        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            val initialSize = dataList.size
            updateDataList(dataList)
            val updatedSize = dataList.size
            recyclerView.post { adapter.notifyItemRangeInserted(initialSize, updatedSize) }
            loading = true
        }
    }
}

이렇게 RecyclerView에 추가하십시오.

recyclerView.addOnScrollListener(OnScrollListener(layoutManager, adapter, dataList))

전체 코드 예제는 이 Github 저장소 를 참조하십시오 .


14
이 솔루션을 GridLayoutManager와 함께 적용하는 방법에 대한 아이디어가 있습니까? 이 findFirstVisibleItemPosition()방법은 LinearLayoutManager에서만 사용할 수 있으므로 작동 방법을 알 수 없습니다.
MathieuMaree

1
추가 정보 :이 경우 사용 visibleItemCount = mLayoutManager.getChildCount();은을 사용하는 것과 동일하게 작동합니다 visibleItemCount = mRecyclerView.getChildCount();.
Jing Li

3
코드를 onScrolled () 메소드 대신 onScrollStateChanged () 메소드에 배치하십시오.
Anirudh

2
@ toobsco42 당신은 이것을 사용할 수 있습니다 :int[] firstVisibleItemPositions = new int[yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
MathieuMaree

1
이되지 해결 findFirstVisibleItemPosition () 란 무엇입니까
수레 쉬 샤르마

72

마지막 항목이 완전히 표시되었을 때만 알림을 받으려는 경우을 사용할 수 있습니다 View.canScrollVertically().

내 구현은 다음과 같습니다.

public abstract class OnVerticalScrollListener
        extends RecyclerView.OnScrollListener {

    @Override
    public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (!recyclerView.canScrollVertically(-1)) {
            onScrolledToTop();
        } else if (!recyclerView.canScrollVertically(1)) {
            onScrolledToBottom();
        } else if (dy < 0) {
            onScrolledUp();
        } else if (dy > 0) {
            onScrolledDown();
        }
    }

    public void onScrolledUp() {}

    public void onScrolledDown() {}

    public void onScrolledToTop() {}

    public void onScrolledToBottom() {}
}

참고 : recyclerView.getLayoutManager().canScrollVertically()API <14를 지원하려는 경우 사용할 수 있습니다 .


이것은 위대하고 간단한 답변입니다! 감사합니다!
Andranik

2
! recyclerView.canScrollVertically (-1)를 확인하여 pull to refresh에 비슷한 솔루션을 사용할 수 있습니다.
LordParsley

최고의 솔루션입니다. 고마워 친구!
Angmar

1
@LordParsley 감사합니다. 이것은 SwipeRefreshLayout이 끌어서 새로 고침을 확인하는 방법 중 하나입니다.
Hai Zhang

1
@EpicPandaForce 사용하는 경우 RecyclerView다시 운이 좋을 것입니다 View.canScrollVertically(). 관련 메소드가에서 보호 대신 공개로 표시되어 있기 때문에 정적 사본을 만들 수 있기 때문 입니다 RecyclerView. 원하는 경우 RecyclerView다시 구현 하도록 확장 할 수도 있습니다 canScrollVertically(). 세 번째로 간단한 방법은 전화하는 것 recyclerView.getLayoutManager().canScrollVertically()입니다. 내 답변에서 수정되었습니다.
Hai Zhang

67

다른 접근법이 있습니다. 모든 레이아웃 관리자와 함께 작동합니다.

  1. 어댑터 클래스를 추상으로 만들기
  2. 그런 다음 어댑터 클래스에서 추상 메소드를 작성하십시오 (예 : load ())
  3. 에서 onBindViewHolder 위치를 선택하면 마지막과 전화 부하 ()
  4. 활동 또는 단편에서 어댑터 오브젝트를 작성하는 동안 load () 함수를 대체하십시오.
  5. 오버로드 된로드 함수에서 loadmore 호출을 구현하십시오.

자세한 이해를 위해 블로그 게시물과 예제 프로젝트를 작성했습니다. http://sab99r.com/blog/recyclerview-endless-load-more/

MyAdapter.java

public abstract class MyAdapter extends RecyclerView.Adapter<ViewHolder>{

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            //check for last item
            if ((position >= getItemCount() - 1))
                load();
        }

        public abstract void load();
}

MyActivity.java

public class MainActivity extends AppCompatActivity {
    List<Items> items;
    MyAdapter adapter;

   @Override
    protected void onCreate(Bundle savedInstanceState) {
    ...
    adapter=new MyAdapter(items){
            @Override
            public void load() {
                //implement your load more here
                Item lastItem=items.get(items.size()-1);
                loadMore();
            }
        };
   }
}

3
솔루션과 마찬가지로 어댑터를 추상화하지 않아도됩니다. 대신 어댑터에서 인터페이스를 만들고 활동을 유연하고 우아하게 유지하도록 구현할 수 있습니다 (예 : ItemDisplayListener => onItemDisplayed (int position)). 이 방법으로 N, N-1, N-2에서 어느 위치 에나 적재 할 수 있습니다.
Samer

1
@mcwise 부울 추가 finish = false; 당신이 끝에 도달하면 finished = true .. 현재 if 조건을 if ((position> = getItemCount ()-1) && (! finished))로 변경
Sabeer Mohammed

1
정답으로 표시해야합니다. 우아하고 간단합니다.
Greg Ennis

2
첫 번째 솔루션의 인기에 묻힌 우수한 솔루션의 좋은 예입니다. 마지막 행은 종종 "로드 중 ..."배너 역할을하는 별도의 뷰 타입에 바인딩됩니다. 이 뷰 유형이 바인드 호출을 받으면 더 많은 데이터를 사용할 수있을 때 데이터 변경 알림을 트리거하는 콜백을 설정합니다. 스크롤 리스너를 연결하는 데 대한 대답은 낭비이며 다른 레이아웃 유형에서는 작동하지 않을 수 있습니다.
Michael Hays

3
@ mcwise 사용자가 적극적으로 스크롤하여 마지막 항목으로 다시 돌아 가면 끝없이 호출됩니다. onBindViewHolder는 뷰가 화면에 들어갈 때마다 한 번만 호출되며 계속 호출하지 않습니다.
David Liu

18

내 대답은 Noor의 수정 된 버전입니다. 나는 내가 가진 ListViewEndlessScrollListener에서 (SO에 대한 많은 대답에서 쉽게 찾을 수있는) 통과하여 과거의 청취자를 쉽게 업데이트하고 RecyclerView싶었습니다 EndlessRecyclScrollListener.

그래서 여기 코드가 도움이되기를 바랍니다.

public abstract class EndlessScrollRecyclListener extends RecyclerView.OnScrollListener
{
    // The total number of items in the dataset after the last load
    private int previousTotalItemCount = 0;
    private boolean loading = true;
    private int visibleThreshold = 5;
    int firstVisibleItem, visibleItemCount, totalItemCount;
    private int startingPageIndex = 0;
    private int currentPage = 0;

    @Override
    public void onScrolled(RecyclerView mRecyclerView, int dx, int dy)
    {
        super.onScrolled(mRecyclerView, dx, dy);
        LinearLayoutManager mLayoutManager = (LinearLayoutManager) mRecyclerView
                .getLayoutManager();

        visibleItemCount = mRecyclerView.getChildCount();
        totalItemCount = mLayoutManager.getItemCount();
        firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
        onScroll(firstVisibleItem, visibleItemCount, totalItemCount);
    }

    public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount)
    {
        // If the total item count is zero and the previous isn't, assume the
        // list is invalidated and should be reset back to initial state
        if (totalItemCount < previousTotalItemCount)
        {
            this.currentPage = this.startingPageIndex;
            this.previousTotalItemCount = totalItemCount;
            if (totalItemCount == 0)
            {
                this.loading = true;
            }
        }
        // If it’s still loading, we check to see if the dataset count has
        // changed, if so we conclude it has finished loading and update the current page
        // number and total item count.
        if (loading && (totalItemCount > previousTotalItemCount))
        {
            loading = false;
            previousTotalItemCount = totalItemCount;
            currentPage++;
        }

        // If it isn’t currently loading, we check to see if we have breached
        // the visibleThreshold and need to reload more data.
        // If we do need to reload some more data, we execute onLoadMore to fetch the data.
        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem +
                visibleThreshold))
        {
            onLoadMore(currentPage + 1, totalItemCount);
            loading = true;
        }
    }

    // Defines the process for actually loading more data based on page
    public abstract void onLoadMore(int page, int totalItemsCount);

}

1
구현하려면 LinearLeayoutManager를 사용하려면 RecyclerView가 필요합니다. 나쁜 생각!
Orri

@Orri 왜 설명해 주시겠습니까? 감사!
Federico Ponzi

1
onScrolled에서 RecyclerView에서 LinearLayoutManager로 LayoutManager를 캐스트합니다. StaggredGridLayout 또는 다른 것에서는 작동하지 않습니다. StaggredGridLayout에 findFirstVisibleItemPosition ()이 없습니다.
Orri

@Orri 지적 해 주셔서 감사합니다! 그러나 나는 그것이 나에게 효과가 있었고 공보에 근거한 다른 사례들 때문에 그렇게 나쁜 생각이라고 생각하지 않는다. :)
Federico Ponzi

10

저에게는 매우 간단합니다.

     private boolean mLoading = false;

     mList.setOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            int totalItem = mLinearLayoutManager.getItemCount();
            int lastVisibleItem = mLinearLayoutManager.findLastVisibleItemPosition();

            if (!mLoading && lastVisibleItem == totalItem - 1) {
                mLoading = true;
                // Scrolled to bottom. Do something here.
                mLoading = false;
            }
        }
    });

비동기 작업에주의하십시오. mLoading은 비동기 작업이 끝날 때 변경되어야합니다. 도움이 되길 바랍니다.


나는 기본적으로 나 자신에게 질문을 한 후에이 대답을 찾았습니다. 이것이 가장 좋은 해결책입니다.
wsgeorge 2016

loadmore 페이지 번호가 android로 전달되는 recyclerview에 대한 솔루션을 제공하십시오
Harsha

getWebServiceData (); mStoresAdapterData.setOnLoadMoreListener (new StoresAdapter.OnLoadMoreListener () {@Override public void onLoadMore () {// null을 추가하므로 어댑터는 view_type을 확인하고 하단 storeList에서 진행률 막대를 표시합니다 .List (add (null); mStoresAdapterData.notifyItemInserted (storesList.size) ()-1); ++ pageNumber; getWebServiceData ();}}); 둘 다 마지막으로 두 번째 페이지 데이터 만 표시됩니다. 제발 도와주세요
Harsha

나는 recyclerView 페이지 매김의 많은 구현을 시도했지만 그들 중 누구도, 내 upvote에 선생님이이 일을 제외하고, 일하지
모하메드 Elrashied

9

대부분의 대답은 , 또는 , 또는 심지어 RecyclerView사용 한다고 가정 하거나 스크롤이 수직 또는 수평이라고 가정하지만, 아무도 완전하게 일반 답변을 게시 한 사람은 없습니다 .LinearLayoutManagerGridLayoutManagerStaggeredGridLayoutManager

ViewHolder의 어댑터를 사용하는 것은 분명 좋은 해결책이 아닙니다. 어댑터를 사용하는 어댑터가 둘 이상있을 수 있습니다 RecyclerView. 내용을 "적응"합니다. 더 많은 항목이 필요하다는 것을 시스템에 알려야 하는 리사이클 러 보기 (현재 사용자에게 표시되는 내용을 담당하는 클래스가 아니라 컨텐츠를 제공하는 역할을하는 어댑터는 RecyclerView아님) 여야합니다. 하중).

다음은 추상화 된 RecyclerView 클래스 (RecycerView.LayoutManager 및 RecycerView.Adapter) 이외의 다른 것을 사용하는 솔루션입니다.

/**
 * Listener to callback when the last item of the adpater is visible to the user.
 * It should then be the time to load more items.
 **/
public abstract class LastItemListener extends RecyclerView.OnScrollListener {

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
      super.onScrolled(recyclerView, dx, dy);

      // init
      RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
      RecyclerView.Adapter adapter = recyclerView.getAdapter();

      if (layoutManager.getChildCount() > 0) {
        // Calculations..
        int indexOfLastItemViewVisible = layoutManager.getChildCount() -1;
        View lastItemViewVisible = layoutManager.getChildAt(indexOfLastItemViewVisible);
        int adapterPosition = layoutManager.getPosition(lastItemViewVisible);
        boolean isLastItemVisible = (adapterPosition == adapter.getItemCount() -1);

        // check
        if (isLastItemVisible)
          onLastItemVisible(); // callback
     }
   }

   /**
    * Here you should load more items because user is seeing the last item of the list.
    * Advice: you should add a bollean value to the class
    * so that the method {@link #onLastItemVisible()} will be triggered only once
    * and not every time the user touch the screen ;)
    **/
   public abstract void onLastItemVisible();

}


// --- Exemple of use ---

myRecyclerView.setOnScrollListener(new LastItemListener() {
    public void onLastItemVisible() {
         // start to load more items here.
    }
}

1
귀하의 답변이 옳고 LayoutManagers한 순간 만 작동했습니다 . 계산 코드를 다른 scrollListener 메소드로 이동하십시오- onScrollStateChanged대신 이동하십시오 onScrolled. 그리고에서 onScrollStateChanged바로 확인 :if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer

조언 해 주셔서 감사하지만 더 나은 솔루션이라고는 생각하지 않습니다. 개발자는 recyclerview가 스크롤을 멈추기 전에 리스너가 트리거되기를 원합니다. 따라서 새 데이터 항목을로드하거나 (데이터가 로컬로 저장된 경우) recyclerview 끝에 진행률 표시 줄 뷰 홀더를 추가하여 recyclerview가 스크롤을 멈 추면 원 진행 상태가 될 수 있습니다. 마지막으로 본 항목으로 -bar.
Yairopro

6

허용되는 답변은 완벽하게 작동하지만 setOnScrollListener가 더 이상 사용되지 않아 변수 수와 if 조건이 줄어들 기 때문에 아래 솔루션은 addOnScrollListener를 사용합니다.

final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
feedsRecyclerView.setLayoutManager(layoutManager);

feedsRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        if (dy > 0) {   
            if ((layoutManager.getChildCount() + layoutManager.findFirstVisibleItemPosition()) >= layoutManager.getItemCount()) {
                Log.d("TAG", "End of list");
                //loadMore();
            }
        }
    }
});

1
때로는 onScrolled ()에서 내 loadMore () 메서드가 세 번 호출되었습니다. 왜 세 번 호출되고 여러 번 호출을 중지하는 방법에 대한 아이디어가 있습니다.
VED

DY> 0은 항상 false입니다
Dushyant Suthar

@ved 스크롤이 스크롤, 스크롤, 유휴 상태의 3 가지 상태이기 때문입니다. 적합한 것을 선택하십시오.
TheRealChx101 1

5

질문에 대한 답변이 너무 많지만 끝없는 목록보기를 만든 경험을 공유하고 싶습니다. 최근 목록을 특정 지점까지 무한대로 스크롤하여주기에서 작동 할 수있는 사용자 지정 Carousel LayoutManager를 구현했습니다. GitHub에 대한 자세한 설명 은 다음과 같습니다 .

사용자 정의 LayoutManager 생성에 대한 짧지 만 귀중한 권장 사항 으로이 기사를 살펴 보는 것이 좋습니다. http://cases.azoft.com/create-custom-layoutmanager-android/


5

Kotlin의 확장 기능을 사용하면 코드가 훨씬 더 우아하게 보일 수 있습니다. 이것을 원하는 곳에 두십시오 (ExtensionFunctions.kt 파일 안에 있습니다).

/**
 * WARNING: This assumes the layout manager is a LinearLayoutManager
 */
fun RecyclerView.addOnScrolledToEnd(onScrolledToEnd: () -> Unit){

    this.addOnScrollListener(object: RecyclerView.OnScrollListener(){

        private val VISIBLE_THRESHOLD = 5

        private var loading = true
        private var previousTotal = 0

        override fun onScrollStateChanged(recyclerView: RecyclerView,
                                          newState: Int) {

            with(layoutManager as LinearLayoutManager){

                val visibleItemCount = childCount
                val totalItemCount = itemCount
                val firstVisibleItem = findFirstVisibleItemPosition()

                if (loading && totalItemCount > previousTotal){

                    loading = false
                    previousTotal = totalItemCount
                }

                if(!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)){

                    onScrolledToEnd()
                    loading = true
                }
            }
        }
    })
}

그런 다음 다음과 같이 사용하십시오.

youRecyclerView.addOnScrolledToEnd {
    //What you want to do once the end is reached
}

이 솔루션은 Kushal Sharma의 답변을 기반으로합니다. 그러나 다음과 같은 이유로 조금 더 좋습니다.

  1. onScrollStateChanged대신을 사용 합니다 onScroll. onScrollRecyclerView에 어떤 종류의 움직임이있을 때마다 호출되는 반면 RecyclerView onScrollStateChanged의 상태가 변경 될 때만 호출 되기 때문에 더 좋습니다 . 를 사용 onScrollStateChanged하면 CPU 시간과 결과적으로 배터리가 절약됩니다.
  2. 이것은 확장 기능을 사용하기 때문에 보유한 모든 RecyclerView에서 사용할 수 있습니다. 클라이언트 코드는 한 줄입니다.

내가 SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working 개인 내부 클래스를 당기면 PullToRefresh : SwipeRefreshLayout.OnRefreshListener {override fun onRefresh () {pbViewMore !!. visibility = View.GONE pageCount = 0 courseList.clear () // 비동기 작업을 여기에 호출 srlNoMessageRefreshLayout !!. isRefreshing = false swipeRefreshLayout! ! .isRefreshing = false}}
Naveen Kumar M

5

이것이 내가 간단하고 짧은 방법입니다.

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
    {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            if(!recyclerView.canScrollVertically(1) && dy != 0)
            {
                // Load more results here

            }
        }
    });

3

OK, RecyclerView.Adapter 의 onBindViewHolder 메소드를 사용하여 수행했습니다 .

어댑터:

public interface OnViewHolderListener {
    void onRequestedLastItem();
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    ...

    if (position == getItemCount() - 1) onViewHolderListener.onRequestedLastItem();
}

조각 (또는 활동) :

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    contentView = inflater.inflate(R.layout.comments_list, container, false);
    recyclerView = (RecyclerView) mContentView.findViewById(R.id.my_recycler_view);
    adapter = new Adapter();
    recyclerView.setAdapter(adapter);

    ...

    adapter.setOnViewHolderListener(new Adapter.OnViewHolderListener() {
        @Override
        public void onRequestedLastItem() {
            //TODO fetch new data from webservice
        }
    });
    return contentView;
}

1
onBind에 의존하는 것은 좋은 생각이 아닙니다. RecyclerView는 캐시보기를 수행하고 일부 레이아웃 관리자에는 사전 캐시 기능이 있습니다. 스크롤 리스너 설정을 제안하고 스크롤 리스너가 중지되면 레이아웃 관리자에서 마지막으로 보이는 항목 위치를 가져옵니다. 기본 레이아웃 관리자를 사용하는 경우 모두 findLastVisible ** 메소드가 있습니다.
yigit

@yigit RecyclerView는 사물을 얼마나 멀리 미리 캐시하려고합니까? 500 개의 아이템이 있다면, 지금까지 캐시되지 않을 것입니다 (그렇지 않으면 성능이 크게 낭비 될 것입니다). 실제로 캐시를 시작하면 (480 개 요소 일지라도) 어쨌든 다른 배치를로드 할 시간입니다.
Alex Orlov

1
먼 위치로 부드럽게 스크롤하지 않는 한 preCache를 방해하지 않습니다. 이 경우 LLM (기본적으로)은 대상보기를 찾아 감속 할 수 있도록 한 페이지 대신 두 페이지를 레이아웃합니다. 범위를 벗어난 캐시 뷰를 수행합니다. 기본적으로이 캐시 크기는 2이며 setItemCacheSize를 통해 구성 할 수 있습니다.
yigit

1
@yigit은 문제를 생각하면서 캐싱이 어떻게 문제를 제시하는지 알 수 없습니다. 우리는 "마지막"항목이 처음으로 바인드 될 때에 만 관심이 있습니다! 레이아웃 관리자가 이미 바인딩 된 캐시 된 뷰를 재사용 할 때 onBindViewHolder에 대한 호출이 누락 된 것은 목록 끝에 더 많은 데이터를 페이징하는 데 관심이있을 때 문제가되지 않습니다. 옳은?
Greg Ennis

2
내 FlexibleAdapter 라이브러리에서 호출을 통해 끝없는 스크롤을 채택하기로 결정했습니다 onBindViewHolder(). 매력처럼 작동합니다. 구현은 스크롤 리스너에 비해 작으며 사용하기 쉽습니다.
Davideas

3

로딩 이벤트를 감지하는 방법은 스크롤을 감지하는 것이 아니라 마지막 뷰가 첨부되었는지 여부를 듣는 것입니다. 마지막보기가 첨부 된 경우 더 많은 콘텐츠를로드하는 타이밍으로 간주합니다.

class MyListener implements RecyclerView.OnChildAttachStateChangeListener {
    RecyclerView mRecyclerView;

    MyListener(RecyclerView view) {
        mRecyclerView = view;
    }

    @Override
    public void onChildViewAttachedToWindow(View view) {

    RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
    RecyclerView.LayoutManager mgr = mRecyclerView.getLayoutManager();
    int adapterPosition = mgr.getPosition(view);

    if (adapterPosition == adapter.getItemCount() - 1) {
        // last view was attached
        loadMoreContent();
    }

    @Override
    public void onChildViewDetachedFromWindow(View view) {}
}

2

사용 된 LayoutManager(예 :)을 확장 LinearLayoutManager하고 scrollVerticallyBy()메서드를 재정의 하려고합니다 . 먼저, 먼저 호출 super한 다음 반환 된 정수 값을 확인합니다. 값이 같으면 0아래쪽 또는 위쪽 테두리에 도달합니다. 그런 다음 findLastVisibleItemPosition()방법을 사용 하여 어느 경계에 도달했는지 확인하고 필요한 경우 더 많은 데이터를로드합니다. 그냥 생각이야

또한 오버 스크롤을 허용하고 "로드 중"표시기를 표시하는 방법에서 값을 반환 할 수도 있습니다.


2
 recyclerList.setOnScrollListener(new RecyclerView.OnScrollListener() 
            {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx,int dy)
                {
                    super.onScrolled(recyclerView, dx, dy); 
                }

                @Override
                public void onScrollStateChanged(RecyclerView recyclerView,int newState) 
                {
                    int totalItemCount = layoutManager.getItemCount();
                    int lastVisibleItem = layoutManager.findLastVisibleItemPosition();

                    if (totalItemCount> 1) 
                    {
                        if (lastVisibleItem >= totalItemCount - 1) 
                        {
                            // End has been reached
                            // do something 
                        }
                    }          
                }
            });  

1
onScrollStateChanged()스크롤 상태가 아닌 스크롤 상태를 변경할 때만 호출되므로 작동하지 않습니다. 이 onScrolled()방법을 사용해야합니다 .
mradzinski

2

클래스 의 onBindViewHolder메소드 에서이 논리를 사용하여 무한 스크롤 유형 구현을 달성했습니다 RecyclerView.Adapter.

    if (position == mItems.size() - 1 && mCurrentPage <= mTotalPageCount) {
        if (mCurrentPage == mTotalPageCount) {
            mLoadImagesListener.noMorePages();
        } else {
            int newPage = mCurrentPage + 1;
            mLoadImagesListener.loadPage(newPage);
        }
    }

이 코드를 사용하면 RecyclerView가 마지막 항목에 도달하면 API에서 더 많은 데이터를로드하고 새 결과를 어댑터에 추가하는 인터페이스에서 현재 페이지와 콜백을 증가시킵니다.

이것이 명확하지 않으면 더 완전한 예제를 게시 할 수 있습니까?


나는이 게시물이 실제로 오래되었다는 것을 알고 있지만, 나는 다소 새롭고 비교할 변수를 어디에서 얻을 수 있는지 모른다. mCurrentPage와 mTotalPageCount는 내가 가정 한 데이터 세트를 참조하고 mItems는 목록 항목의 배열 목록이지만 어떻게 위치를 찾을 수 있습니까?
BooleanCheese

내가 어떻게 RecyclerView.Adapter를 구현했는지 볼 수 있습니다 : github.com/dominicthomas/FlikrGridRecyclerView/blob/master/app/…
speedynomads

2

StaggeredGridLayoutManager 를 사용하는 사람들을 위해 여기에 내 구현이 있습니다.

 private class ScrollListener extends RecyclerView.OnScrollListener {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

        firstVivisibleItems = mLayoutManager.findFirstVisibleItemPositions(firstVivisibleItems);

        if(!recyclerView.canScrollVertically(1) && firstVivisibleItems[0]!=0) {
            loadMoreImages();
        }

    }

    private boolean loadMoreImages(){
        Log.d("myTag", "LAST-------HERE------");
        return true;
    }
}

@SudheeshMohan 여기 에 풀 클래스가 있습니다. 작습니다) 내 프로젝트에서 스팬 수를 동적으로 변경하고 1 및 2 스팬 수에 대해 작동합니다.
Dennis Zinkovski

recyclerView.canScrollVertically (1)는 그렇게하지만 API 14가 필요합니다. :(
Sudheesh Mohan

2

paginate 라는 이름의 라이브러리를 쉽게 사용할 수 있습니다 . ListView와 RecyclerView (LinearLayout, GridLayout 및 StaggeredGridLayout)를 모두 지원합니다.

다음은 Github의 프로젝트에 대한 링크입니다


2
  1. 추상 클래스를 만들고 RecyclerView.OnScrollListener를 확장합니다.

    public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
    private int previousTotal = 0;
    private boolean loading = true;
    private int visibleThreshold;
    private int firstVisibleItem, visibleItemCount, totalItemCount;
    private RecyclerView.LayoutManager layoutManager;
    
    public EndlessRecyclerOnScrollListener(RecyclerView.LayoutManager layoutManager, int visibleThreshold) {
    this.layoutManager = layoutManager; this.visibleThreshold = visibleThreshold;
    }
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);
    
    visibleItemCount = recyclerView.getChildCount();
    totalItemCount = layoutManager.getItemCount();
    firstVisibleItem = ((LinearLayoutManager)layoutManager).findFirstVisibleItemPosition();
    
    if (loading) {
        if (totalItemCount > previousTotal) {
            loading = false;
            previousTotal = totalItemCount;
        }
    }
    if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
        onLoadMore();
        loading = true;
    }
      }
    
    public abstract void onLoadMore();}
  2. 활동 (또는 조각)에서 addOnScrollListener를 recyclerView에 추가하십시오.

    LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager, 3) {
        @Override
        public void onLoadMore() {
            //TODO
            ...
        }
    });

1

RecyclerView로 페이지를 매기는 방법에 대한 자세한 예가 있습니다. 높은 수준에서 PAGE_SIZE 세트를 가지고 30이라고 말합니다. 그래서 30 개의 항목을 요청하고 30을 다시 받으면 다음 페이지를 요청합니다. 30 개 미만의 항목을 받으면 마지막 페이지에 도달했음을 나타내는 변수에 플래그를 지정한 다음 더 많은 페이지 요청을 중단합니다. 그것을 확인하고 당신의 생각을 알려주십시오.

https://medium.com/@etiennelawlor/pagination-with-recyclerview-1cb7e66a502b


1

웹에서 AsyncListUtil 을 사용하는 내 솔루션은 다음과 같이 말합니다.이 클래스는 단일 스레드를 사용하여 데이터를로드하므로 네트워크와 같은 보조 스토리지 (예 : 네트워크는 아님)에서 데이터를로드하는 데 적합합니다. 그러나 나는 데이터를 읽고 잘 작동하기 위해 odata를 사용하고 있습니다. 예제 데이터 엔터티와 네트워크 방법이 누락되었습니다. 예제 어댑터 만 포함합니다.

public class AsyncPlatoAdapter extends RecyclerView.Adapter {

    private final AsyncPlatoListUtil mAsyncListUtil;
    private final MainActivity mActivity;
    private final RecyclerView mRecyclerView;
    private final String mFilter;
    private final String mOrderby;
    private final String mExpand;

    public AsyncPlatoAdapter(String filter, String orderby, String expand, RecyclerView recyclerView, MainActivity activity) {
        mFilter = filter;
        mOrderby = orderby;
        mExpand = expand;
        mRecyclerView = recyclerView;
        mActivity = activity;
        mAsyncListUtil = new AsyncPlatoListUtil();

    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).
                inflate(R.layout.plato_cardview, parent, false);

        // Create a ViewHolder to find and hold these view references, and
        // register OnClick with the view holder:
        return new PlatoViewHolderAsync(itemView, this);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        final Plato item = mAsyncListUtil.getItem(position);
        PlatoViewHolderAsync vh = (PlatoViewHolderAsync) holder;
        if (item != null) {
            Integer imagen_id = item.Imagen_Id.get();
            vh.getBinding().setVariable(BR.plato, item);
            vh.getBinding().executePendingBindings();
            vh.getImage().setVisibility(View.VISIBLE);
            vh.getProgress().setVisibility(View.GONE);
            String cacheName = null;
            String urlString = null;
            if (imagen_id != null) {
                cacheName = String.format("imagenes/imagen/%d", imagen_id);
                urlString = String.format("%s/menusapi/%s", MainActivity.ROOTPATH, cacheName);
            }
            ImageHelper.downloadBitmap(mActivity, vh.getImage(), vh.getProgress(), urlString, cacheName, position);
        } else {
            vh.getBinding().setVariable(BR.plato, item);
            vh.getBinding().executePendingBindings();
            //show progress while loading.
            vh.getImage().setVisibility(View.GONE);
            vh.getProgress().setVisibility(View.VISIBLE);
        }
    }

    @Override
    public int getItemCount() {
        return mAsyncListUtil.getItemCount();
    }

    public class AsyncPlatoListUtil extends AsyncListUtil<Plato> {
        /**
         * Creates an AsyncListUtil.
         */
        public AsyncPlatoListUtil() {
            super(Plato.class, //my data class
                    10, //page size
                    new DataCallback<Plato>() {
                        @Override
                        public int refreshData() {
                            //get count calling ../$count ... odata endpoint
                            return countPlatos(mFilter, mOrderby, mExpand, mActivity);
                        }

                        @Override
                        public void fillData(Plato[] data, int startPosition, int itemCount) {
                            //get items from odata endpoint using $skip and $top
                            Platos p = loadPlatos(mFilter, mOrderby, mExpand, startPosition, itemCount, mActivity);
                            for (int i = 0; i < Math.min(itemCount, p.value.size()); i++) {
                                data[i] = p.value.get(i);
                            }

                        }
                    }, new ViewCallback() {
                        @Override
                        public void getItemRangeInto(int[] outRange) {
                            //i use LinearLayoutManager in the RecyclerView
                            LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
                            outRange[0] = layoutManager.findFirstVisibleItemPosition();
                            outRange[1] = layoutManager.findLastVisibleItemPosition();
                        }

                        @Override
                        public void onDataRefresh() {
                            mRecyclerView.getAdapter().notifyDataSetChanged();
                        }

                        @Override
                        public void onItemLoaded(int position) {
                            mRecyclerView.getAdapter().notifyItemChanged(position);
                        }
                    });
            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    onRangeChanged();
                }
            });
        }
    }
}

1

트윗 담아 가기

왜이 논리를 사용하지 않습니까?

public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    int totalItemCount, lastVisibleItemPosition;

    if (dy > 0) {
      totalItemCount = _layoutManager.getItemCount();
      lastVisibleItemPosition = _layoutManager.findLastVisibleItemPosition();

      if (!_isLastItem) {
        if ((totalItemCount - 1) == lastVisibleItemPosition) {
          LogUtil.e("end_of_list");

          _isLastItem = true;
        }
      }
    }
  }

내 onScroller ()는 단일 항목에 대해 3 번을 호출하여 end_of_list 3 번과 페이지 매김 논리를 3 번으로 얻습니다. 페이지 매김을 한 번만 호출하도록이 문제를 해결하는 방법
ved

@ved 그런 일은 일어나지 않아야합니다. 위의 코드는 end_of_list조건이 한 번만 호출 되도록합니다 . 플래그 _isLastItem는 활동의 전역 변수이며 기본값은 false입니다. 내가 한 일은 목록 끝을 감지하고 다음 페이지를 검색 한 다음 어댑터에 새 데이터 세트를 알리고 마지막으로 _isLastItem다시 false로 설정 한 후 백그라운드 작업을 실행하는 것 입니다.
leonapse

1

아래에서 시도하십시오 :

import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutManager;

/**
 * Abstract Endless ScrollListener
 * 
 */
public abstract class EndlessScrollListener extends
        RecyclerView.OnScrollListener {
    // The minimum amount of items to have below your current scroll position
    // before loading more.
    private int visibleThreshold = 10;
    // The current offset index of data you have loaded
    private int currentPage = 1;
    // True if we are still waiting for the last set of data to load.
    private boolean loading = true;
    // The total number of items in the data set after the last load
    private int previousTotal = 0;
    private int firstVisibleItem;
    private int visibleItemCount;
    private int totalItemCount;
    private LayoutManager layoutManager;

    public EndlessScrollListener(LayoutManager layoutManager) {
        validateLayoutManager(layoutManager);
        this.layoutManager = layoutManager;
    }

    public EndlessScrollListener(int visibleThreshold,
            LayoutManager layoutManager, int startPage) {
        validateLayoutManager(layoutManager);
        this.visibleThreshold = visibleThreshold;
        this.layoutManager = layoutManager;
        this.currentPage = startPage;
    }

    private void validateLayoutManager(LayoutManager layoutManager)
            throws IllegalArgumentException {
        if (null == layoutManager
                || !(layoutManager instanceof GridLayoutManager)
                || !(layoutManager instanceof LinearLayoutManager)) {
            throw new IllegalArgumentException(
                    "LayoutManager must be of type GridLayoutManager or LinearLayoutManager.");
        }
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        visibleItemCount = recyclerView.getChildCount();
        totalItemCount = layoutManager.getItemCount();
        if (layoutManager instanceof GridLayoutManager) {
            firstVisibleItem = ((GridLayoutManager) layoutManager)
                    .findFirstVisibleItemPosition();
        } else if (layoutManager instanceof LinearLayoutManager) {
            firstVisibleItem = ((LinearLayoutManager) layoutManager)
                    .findFirstVisibleItemPosition();
        }
        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading
                && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            // End has been reached do something
            currentPage++;
            onLoadMore(currentPage);
            loading = true;
        }
    }

    // Defines the process for actually loading more data based on page
    public abstract void onLoadMore(int page);

}

1

Abdulaziz Noor Answer를 사용하여 LoadMoreRecyclerView를 만들었습니다.

LoadMoreRecyclerView

public class LoadMoreRecyclerView extends RecyclerView  {

    private boolean loading = true;
    int pastVisiblesItems, visibleItemCount, totalItemCount;
    //WrapperLinearLayout is for handling crash in RecyclerView
    private WrapperLinearLayout mLayoutManager;
    private Context mContext;
    private OnLoadMoreListener onLoadMoreListener;

    public LoadMoreRecyclerView(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }

    public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        init();
    }

    private void init(){
        mLayoutManager = new WrapperLinearLayout(mContext,LinearLayoutManager.VERTICAL,false);
        this.setLayoutManager(mLayoutManager);
        this.setItemAnimator(new DefaultItemAnimator());
        this.setHasFixedSize(true);
    }

    @Override
    public void onScrolled(int dx, int dy) {
        super.onScrolled(dx, dy);

        if(dy > 0) //check for scroll down
        {
            visibleItemCount = mLayoutManager.getChildCount();
            totalItemCount = mLayoutManager.getItemCount();
            pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();

            if (loading)
            {
                if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount)
                {
                    loading = false;
                    Log.v("...", "Call Load More !");
                    if(onLoadMoreListener != null){
                        onLoadMoreListener.onLoadMore();
                    }
                    //Do pagination.. i.e. fetch new data
                }
            }
        }
    }

    @Override
    public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);
    }

    public void onLoadMoreCompleted(){
        loading = true;
    }

    public void setMoreLoading(boolean moreLoading){
        loading = moreLoading;
    }

    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
        this.onLoadMoreListener = onLoadMoreListener;
    }
}

래퍼 선형 레이아웃

public class WrapperLinearLayout extends LinearLayoutManager
{
    public WrapperLinearLayout(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e) {
            Log.e("probe", "meet a IOOBE in RecyclerView");
        }
    }
}

// 같은 XML로 추가

<your.package.LoadMoreRecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</your.package.LoadMoreRecyclerView>

OnCreate 또는 onViewCreated

mLoadMoreRecyclerView = (LoadMoreRecyclerView) view.findViewById(R.id.recycler_view);
mLoadMoreRecyclerView.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore() {
                callYourService(StartIndex);
            }
        });

callYourService

private void callYourService(){
    //callyour Service and get response in any List

    List<AnyModelClass> newDataFromServer = getDataFromServerService();
    //Enable Load More
    mLoadMoreRecyclerView.onLoadMoreCompleted();

    if(newDataFromServer != null && newDataFromServer.size() > 0){
            StartIndex += newDataFromServer.size();

            if (newDataFromServer.size() < Integer.valueOf(MAX_ROWS)) {
                    //StopLoading..
                   mLoadMoreRecyclerView.setMoreLoading(false);
            }
    }
    else{
            mLoadMoreRecyclerView.setMoreLoading(false);
            mAdapter.notifyDataSetChanged();
    }
}

1

데이터 모델의 순수한 논리 만 사용하여 스크롤 리스너없이 구현할 수도 있습니다. 스크롤보기는 최대 항목 수뿐만 아니라 위치별로 항목을 가져와야합니다. 모델은 필요한 항목을 하나씩이 아니라 청크 단위로 가져 오는 백그라운드 로직을 가지고 백그라운드 스레드에서이를 수행하여 데이터가 준비되면 뷰에 알립니다.

이 접근 방식을 사용하면 이전 (종종 이미 스크롤 된) 제출보다 최근에 요청 된 (따라서 현재 보이는) 항목을 선호하는 페치 큐를 사용하고 사용할 병렬 스레드 수 등을 제어 할 수 있습니다. 이 방법에 대한 전체 소스 코드 (데모 앱 및 재사용 가능한 라이브러리)는 여기에서 확인할 수 있습니다 .


0

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29에 메소드 public void setOnScrollListener (RecyclerView.OnScrollListener listener)있습니다 . 그것을 사용하십시오

편집하다:

onScrollStateChanged내부의 메서드를 재정의 onScrollListener하고이 작업을 수행하십시오.

            boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount;

            //loading is used to see if its already loading, you have to manually manipulate this boolean variable
            if (loadMore && !loading) {
                 //end of list reached
            }

1
좀 더 구체적으로 말씀해주세요! RecyclerView.OnScrollListener 를 사용 하여 사용자가 목록 끝으로 스크롤되었는지 확인 하려면 어떻게합니까 ?
erdna

RecyclerView.OnScrollListener 에는 onScroll이 없습니다 ! AbsListView.OnScrollListener와 RecyclerView.OnScrollListener를 혼합합니다.
erdna

나중에해야합니다onScrollStateChanged
Panther

onScrollStateChanged스크롤 상태가 아닌 스크롤 상태를 변경할 때만 호출되므로 작동하지 않습니다.
Pedro Oliveira

@PedroOliveira onScrollStateChanged사용자가 마지막 항목으로 스크롤하는지 확인할 수 있습니까?
erdna

0

이 확인 모든 일이 자세히 설명되어 있습니다 : 매김은에서 Z에 RecyclerView를 사용하여

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView,
                                     int newState) {
        super.onScrollStateChanged(recyclerView, newState);
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        int visibleItemCount = mLayoutManager.getChildCount();
        int totalItemCount = mLayoutManager.getItemCount();
        int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();

        if (!mIsLoading && !mIsLastPage) {
            if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount
                    && firstVisibleItemPosition >= 0) {
                loadMoreItems();
            }
        }
    }
})

loadMoreItems () :

private void loadMoreItems() {
    mAdapter.removeLoading();
    //load data here from the server

    // in case of success
    mAdapter.addData(data);
    // if there might be more data
    mAdapter.addLoading();
}

MyAdapter에서 :

private boolean mIsLoadingFooterAdded = false;

public void addLoading() {
    if (!mIsLoadingFooterAdded) {
        mIsLoadingFooterAdded = true;
        mLineItemList.add(new LineItem());
        notifyItemInserted(mLineItemList.size() - 1);
    }
}

public void removeLoading() {
    if (mIsLoadingFooterAdded) {
        mIsLoadingFooterAdded = false;
        int position = mLineItemList.size() - 1;
        LineItem item = mLineItemList.get(position);

        if (item != null) {
            mLineItemList.remove(position);
            notifyItemRemoved(position);
        }
    }
}

public void addData(List<YourDataClass> data) {

    for (int i = 0; i < data.size(); i++) {
        YourDataClass yourDataObject = data.get(i);
        mLineItemList.add(new LineItem(yourDataObject));
        notifyItemInserted(mLineItemList.size() - 1);
    }
}

링크에 답변이 포함되어있을 수 있지만 게시물의 링크에서 설명의 일부를 설명해보십시오.
Ian

0

목록이 너무 작거나 그렇지 않으면 이러한 답변 중 어느 것도 고려하지 않습니다.

다음은 RecycleViews에서 양방향으로 작동하는 코드입니다.

@Override
    public boolean onTouchEvent(MotionEvent motionEvent) {

        if (recyclerViewListener == null) {
            return super.onTouchEvent(motionEvent);
        }

        /**
         * If the list is too small to scroll.
         */
        if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
            if (!canScrollVertically(1)) {
                recyclerViewListener.reachedBottom();
            } else if (!canScrollVertically(-1)) {
                recyclerViewListener.reachedTop();
            }
        }

        return super.onTouchEvent(motionEvent);
    }

    public void setListener(RecyclerViewListener recycleViewListener) {
        this.recyclerViewListener = recycleViewListener;
        addOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                if (recyclerViewListener == null) {
                    return;
                }

                recyclerViewListener.scrolling(dy);

                if (!canScrollVertically(1)) {
                    recyclerViewListener.reachedBottom();
                } else if (!canScrollVertically(-1)) {
                    recyclerViewListener.reachedTop();
                }
            }

        });
    }

0

나는 당신에게 내 근접을 보자. 나를 위해 잘 작동합니다.

도움이 되길 바랍니다.

/**
 * Created by Daniel Pardo Ligorred on 03/03/2016.
 */
public abstract class BaseScrollListener extends RecyclerView.OnScrollListener {

    protected RecyclerView.LayoutManager layoutManager;

    public BaseScrollListener(RecyclerView.LayoutManager layoutManager) {

        this.layoutManager = layoutManager;

        this.init();
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

        super.onScrolled(recyclerView, dx, dy);

        this.onScroll(recyclerView, this.getFirstVisibleItem(), this.layoutManager.getChildCount(), this.layoutManager.getItemCount(), dx, dy);
    }

    private int getFirstVisibleItem(){

        if(this.layoutManager instanceof LinearLayoutManager){

            return ((LinearLayoutManager) this.layoutManager).findFirstVisibleItemPosition();
        } else if (this.layoutManager instanceof StaggeredGridLayoutManager){

            int[] spanPositions = null; //Should be null -> StaggeredGridLayoutManager.findFirstVisibleItemPositions makes the work.

            try{

                return ((StaggeredGridLayoutManager) this.layoutManager).findFirstVisibleItemPositions(spanPositions)[0];
            }catch (Exception ex){

                // Do stuff...
            }
        }

        return 0;
    }

    public abstract void init();

    protected abstract void onScroll(RecyclerView recyclerView, int firstVisibleItem, int visibleItemCount, int totalItemCount, int dx, int dy);

}

0

이 솔루션은 나에게 완벽하게 작동합니다.

//Listener    

public abstract class InfiniteScrollListener     extendsRecyclerView.OnScrollListener {

public static String TAG = InfiniteScrollListener.class.getSimpleName();
int firstVisibleItem, visibleItemCount, totalItemCount;
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 1;
private int current_page = 1;

private LinearLayoutManager mLinearLayoutManager;

public InfiniteScrollListener(LinearLayoutManager linearLayoutManager) {
    this.mLinearLayoutManager = linearLayoutManager;
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);

    visibleItemCount = recyclerView.getChildCount();
    totalItemCount = mLinearLayoutManager.getItemCount();
    firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();

    if (loading) {
        if (totalItemCount > previousTotal) {
            loading = false;
            previousTotal = totalItemCount;
        }
    }
    if (!loading && (totalItemCount - visibleItemCount - firstVisibleItem <= visibleThreshold)) {

        current_page++;

        onLoadMore(current_page);

        loading = true;
    }
}

public void resetState() {
    loading = true;
    previousTotal = 0;
    current_page = 1;
}

public abstract void onLoadMore(int current_page);
}

//Implementation into fragment 
 private InfiniteScrollListener scrollListener;

scrollListener = new InfiniteScrollListener(manager) {
        @Override
        public void onLoadMore(int current_page) {
            //Load data
        }
    };
    rv.setLayoutManager(manager);
    rv.addOnScrollListener(scrollListener);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.