it-swarm.com.de

Wie implementiere ich eine endlose Liste mit RecyclerView?

Ich möchte ListView in RecyclerView ändern. Ich möchte die onScroll der OnScrollListener in RecyclerView verwenden, um festzustellen, ob ein Benutzer an das Ende der Liste gescrollt wurde. 

Woher weiß ich, ob ein Benutzer zum Ende der Liste blättert, damit ich neue Daten von einem REST - Dienst abrufen kann?

304
erdna

Danke an @Kushal und so habe ich es umgesetzt

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

Vergiss nicht hinzuzufügen

LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
375
Abdulaziz Noor

Machen Sie diese Variablen.

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

Auf Blättern für Recycling-Ansicht einstellen.

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

Hinweis: Stellen Sie sicher, dass Sie LinearLayoutManager als Layoutmanager für RecyclerView verwenden.

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

und für ein Gitter

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

Viel Spaß mit deinen endlosen Rollen !! ^. ^

Update: mRecyclerView.setOnScrollListener () ist veraltet einfach durch mRecyclerView.addOnScrollListener() ersetzen und die Warnung ist weg! Sie können mehr aus dieser SO - Frage lesen.

Da Android Kotlin jetzt offiziell unterstützt, gibt es hier ein Update für dasselbe -

OnScrollListener erstellen 

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

und fügen Sie es so zu Ihrer RecyclerView hinzu 

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

Für ein vollständiges Codebeispiel verweisen Sie auf dieses Github-Repo .

155
Kushal Sharma

Für diejenigen, die nur benachrichtigt werden möchten, wenn das letzte Element vollständig angezeigt wird, können Sie View.canScrollVertically() verwenden.

Hier ist meine Implementierung:

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

Hinweis: Sie können recyclerView.getLayoutManager().canScrollVertically() verwenden, wenn Sie API <14 unterstützen möchten.

70

Hier ist ein anderer Ansatz. Es funktioniert mit jedem Layout-Manager.

  1. Machen Sie die Adapterklasse abstrakt
  2. Dann erstellen Sie eine abstrakte Methode in der Adapterklasse (zB load ())
  3. Überprüfen Sie in onBindViewHolder die Position, wenn Sie zuletzt aufgerufen wurde, und rufen Sie load () auf.
  4. Überschreiben Sie die load () - Funktion, während Sie das Adapterobjekt in Ihrer Aktivität oder Ihrem Fragment erstellen.
  5. Implementieren Sie in der Overided-Load-Funktion Ihren loadmore-Aufruf

Zum Verständnis habe ich einen Blogbeitrag geschrieben und ein Beispielprojekt erhalten Sie hier http://sab99r.com/blog/recyclerview-endless-load-more/

MeinAdapter.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();
}

MeineAktivität.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();
            }
        };
   }
}
59
Sabeer Mohammed

Meine Antwort ist eine modifizierte Version von Noor. Ich ging von einer ListView, wo ich EndlessScrollListener hatte (die Sie leicht in vielen Antworten auf SO finden können) an eine RecyclerView übergeben, also wollte ich, dass eine EndlessRecyclScrollListener meinen früheren Zuhörer leicht aktualisiert.

Also hier ist der Code, hoffe es hilft:

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

}
17
Federico Ponzi

Für mich ist es sehr einfach: 

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

Seien Sie vorsichtig bei asynchronen Jobs: Das Laden von mL muss am Ende der asynchronen Jobs geändert werden. Hoffe es wird hilfreich sein!

10
Minh Nguyen

Die meisten Antworten gehen davon aus, dass RecyclerView eine LinearLayoutManager oder GridLayoutManager oder sogar StaggeredGridLayoutManager verwendet oder davon ausgeht, dass das Scrollen vertikal oder horyzontal ist, aber niemand hat eine vollständig generische Antwort gepostet .

Die Verwendung des Adapters von ViewHolder ist offensichtlich keine gute Lösung. Ein Adapter kann mehr als 1 RecyclerView verwenden. Es "passt" ihren Inhalt an. Es sollte der Recycler View sein (dies ist die Klasse, die für das verantwortlich ist, was dem Benutzer aktuell angezeigt wird, und nicht der Adapter, der nur für die Bereitstellung von Inhalten für die RecyclerView verantwortlich ist), die Ihr system benachrichtigen muss dass mehr Elemente benötigt werden (zum Laden).

Hier ist meine Lösung, die nichts anderes als die abstrahierten Klassen der RecyclerView (RecycerView.LayoutManager und RecycerView.Adapter) verwendet: 

/**
 * 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.
    }
}
9
Yairopro

Obwohl die akzeptierte Antwort einwandfrei funktioniert, verwendet die Lösung unten addOnScrollListener, da setOnScrollListener veraltet ist und die Anzahl der Variablen sowie if-Bedingungen reduziert.

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();
            }
        }
    }
});
6
capt.swag

Obwohl es so viele Antworten auf die Frage gibt, möchte ich gerne unsere Erfahrung mit der Erstellung der endlosen Listenansicht teilen. Wir haben vor kurzem einen benutzerdefinierten Karussell-Layout-Manager implementiert, der im Zyklus arbeiten kann, indem er die Liste stufenlos bis zu einem bestimmten Punkt durchblättert. Hier ist eine detaillierte Beschreibung auf GitHub .

Ich schlage vor, dass Sie sich diesen Artikel mit kurzen, aber wertvollen Empfehlungen zum Erstellen benutzerdefinierter LayoutManager ansehen: http://cases.azoft.com/create-custom-layoutmanager-Android/

Das Erkennen eines Ladeereignisses besteht nicht darin, das Scrollen zu erkennen, sondern zu hören, ob die letzte Ansicht angehängt wurde. Wenn die letzte Ansicht angehängt wurde, halte ich es für ein Timing, mehr Inhalt zu laden.

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) {}
}
3
walkingice

Ich würde versuchen, die verwendete LayoutManager (z. B. LinearLayoutManager) zu erweitern und die scrollVerticallyBy()-Methode zu überschreiben. Zuerst würde ich zuerst super aufrufen und dann den zurückgegebenen Integer-Wert prüfen. Wenn der Wert 0 entspricht, ist ein unterer oder oberer Rand erreicht. Dann würde ich die findLastVisibleItemPosition()-Methode verwenden, um herauszufinden, welche Grenze erreicht ist, und bei Bedarf weitere Daten zu laden. Nur eine Idee.

Darüber hinaus können Sie sogar Ihren Wert aus dieser Methode zurückgeben, um das Überscrollen zu ermöglichen und das Symbol "Laden" anzuzeigen.

2

Es gibt eine einfach zu verwendende Bibliothek mit dem Namen paginate. Unterstützt sowohl ListView als auch RecyclerView (LinearLayout, GridLayout und StaggeredGridLayout).

Hier ist das link zum Projekt auf Github

2
nonybrighto

Für Leute, die StaggeredGridLayoutManager verwenden hier ist meine Implementierung, es funktioniert für mich.

 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;
    }
}
2

Ich habe eine unendliche Scroll-Typ-Implementierung mit dieser Logik in der onBindViewHolder-Methode meiner RecyclerView.Adapter-Klasse erreicht. 

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

Mit diesem Code erhöht RecyclerView die letzte Seite und die Rückrufe auf einer Schnittstelle, die dafür verantwortlich ist, mehr Daten aus der API zu laden und die neuen Ergebnisse dem Adapter hinzuzufügen. 

Ich kann ein vollständigeres Beispiel posten, wenn dies nicht klar ist.

2
speedynomads

Mit den leistungsstarken Erweiterungsfunktionen von Kotlin kann der Code eleganter aussehen. Fügen Sie dies an einer beliebigen Stelle ein (ich habe es in einer ExtensionFunctions.kt-Datei):

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

Und benutze es dann so:

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

Diese Lösung basiert auf der Antwort von Kushal Sharma. Dies ist jedoch etwas besser, weil:

  1. Es verwendet onScrollStateChanged anstelle von onScroll. Dies ist besser, weil onScroll jedes Mal aufgerufen wird, wenn sich irgendeine Art von Bewegung in der RecyclerView befindet, während onScrollStateChanged nur aufgerufen wird, wenn der Status der RecyclerView geändert wird. Durch die Verwendung von onScrollStateChanged sparen Sie CPU-Zeit und folglich auch den Akku.
  2. Da hier Erweiterungsfunktionen verwendet werden, kann dies in jedem RecyclerView verwendet werden. Der Clientcode ist nur eine Zeile.
2
Tiago

OK, ich habe es mit der onBindViewHolder Methode von RecyclerView.Adapter gemacht.

Adapter:

public interface OnViewHolderListener {
    void onRequestedLastItem();
}

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

    ...

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

Fragment (oder Aktivität) :

@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;
}
2
erdna

Versuchen Sie unten:

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

Ich habe ein ziemlich ausführliches Beispiel, wie man mit einer RecyclerView paginiert. Auf einer hohen Ebene habe ich eine PAGE_SIZE, sagen wir 30. Ich fordere 30 Elemente an und wenn ich 30 zurückbekomme, fordere ich die nächste Seite an. Wenn ich weniger als 30 Elemente bekomme, markiere ich eine Variable, um anzuzeigen, dass die letzte Seite erreicht wurde. Dann höre ich auf, weitere Seiten anzufordern. Schau es dir an und lass mich wissen, was du denkst.

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

1
toobsco42

So mache ich es, einfach und kurz:

    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

            }
        }
    });
1
John T

Ich habe LoadMoreRecyclerView erstellt mit Abdulaziz Noor Antwort

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

WrapperLinearLayout

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

// Füge es in xml hinzu

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

OnCreate oder 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
Xar E Ahmer

@kushal @abdulaziz 

Warum nicht diese Logik verwenden?

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;
        }
      }
    }
  }
1
leonapse

Hier sagt meine Lösung mit AsyncListUtil im Web: Beachten Sie, dass diese Klasse einen einzelnen Thread zum Laden der Daten verwendet. Daher eignet sie sich zum Laden von Daten aus einem sekundären Speicher, z. B. einer Festplatte, aber nicht aus dem Netzwerk. aber ich verwende odata, um die daten zu lesen und zu funktionieren. Ich vermisse in meinem Beispiel Datenentitäten und Netzwerkmethoden. Ich füge nur den Beispieladapter hinzu.

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
FRL

Diese Lösung funktioniert perfekt für mich.

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

Funktioniert mit allen Layout-Managern.

import androidx.recyclerview.widget.RecyclerView

/**
 * Created by fatihsantalu on 21.12.2018
 */

class EndlessScrollListener(
  private val block: (Int) -> Unit
) : RecyclerView.OnScrollListener() {

  private var lastPosition = 0

  override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
    super.onScrollStateChanged(recyclerView, newState)
    if (newState != RecyclerView.SCROLL_STATE_IDLE) return

    recyclerView.layoutManager?.let { layoutManager ->
      val lastItem = layoutManager.findViewByPosition(layoutManager.itemCount.minus(1))
      lastItem?.let { view ->
        val lastItemAdapterPosition = layoutManager.getPosition(view)
        lastItemAdapterPosition
          .takeIf {
            layoutManager.isViewPartiallyVisible(view, true, false)
                && it != lastPosition
          }
          ?.let {
            lastPosition = it
            block.invoke(it)
          }
      }
    }
  }
}

Verwendungszweck:

recyclerView.addOnScrollListener(EndlessScrollListener {
    // load more
})
0
santalu

Keine dieser Antworten berücksichtigt, ob die Liste zu klein ist oder nicht.

Hier ist ein Stück Code, den ich verwendet habe und der in beiden Richtungen für RecycleViews funktioniert.

@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
Oliver Dixon

Es ist auch möglich, ohne den Scroll-Listener nur die reine Logik des Datenmodells zu implementieren. Für die Bildlaufansicht müssen Elemente nach Position sowie die maximale Elementanzahl abgerufen werden. Das Modell kann über die Hintergrundlogik verfügen, um die erforderlichen Elemente in Abschnitten statt einzeln nacheinander abzurufen und dies im Hintergrund-Thread auszuführen, um die Ansicht zu benachrichtigen, wenn die Daten bereit sind.

Dieser Ansatz ermöglicht es, die Abrufwarteschlange zu haben, die die zuletzt angeforderten (so aktuell sichtbaren) Elemente gegenüber älteren (wahrscheinlich bereits weggerollten) Übermittlungen bevorzugt, die Anzahl der zu verwendenden parallelen Threads und dergleichen steuert. Der vollständige Quellcode für diesen Ansatz (Demo-App und wiederverwendbare Bibliothek) ist verfügbar here .

0
h22

Hier ist ein Beispiel für die einfache Implementierung von Endless Scrolling RecyclerView unter Verwendung einer aus den verschiedenen Quellen zusammengestellten Simple Library.

Fügen Sie diese Zeile in build.gradle hinzu.

implementation 'com.hereshem.lib:awesomelib:2.0.1'

Erstellen Sie ein RecyclerView-Layout in Activity with

<com.hereshem.lib.recycler.MyRecyclerView
        Android:id="@+id/recycler"
        app:layoutManager="LinearLayoutManager"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"/>

Erstellen Sie einen ViewHolder, indem Sie die unterstützte Klasse übergeben

public static class EVHolder extends MyViewHolder<Events> {
    TextView date, title, summary;
    public EVHolder(View v) {
        super(v);
        date = v.findViewById(R.id.date);
        title = v.findViewById(R.id.title);
        summary = v.findViewById(R.id.summary);
    }
    @Override
    public void bindView(Events c) {
        date.setText(c.date);
        title.setText(c.title);
        summary.setText(c.summary);
    }
}

Erstellen Sie Artikellistenvariable und Adapter mit sehr wenigen Zeilen, indem Sie Elemente, Klassen und Layout im Adapter übergeben

List<Events> items = new ArrayList<>();
MyRecyclerView recycler = findViewById(R.id.recycler);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, items, EVHolder.class, R.layout.row_event);
recycler.setAdapter(adapter);

ClickListener und LoadMore Listener können mit folgenden Zeilen hinzugefügt werden

recycler.setOnItemClickListener(new MyRecyclerView.OnItemClickListener() {
    @Override
    public void onItemClick(int position) {
        Toast.makeText(MainActivity.this, "Recycler Item Clicked " + position, Toast.LENGTH_SHORT).show();
    }
});

recycler.setOnLoadMoreListener(new MyRecyclerView.OnLoadMoreListener() {
    @Override
    public void onLoadMore() {
        loadData();
    }
});
loadData();

Nach dem Laden der Daten muss dies aufgerufen werden

recycler.loadComplete();

Wenn kein LoadMore erforderlich ist, kann das LoadMore-Layout durch Aufruf ausgeblendet werden

recycler.hideLoadMore();

Weitere Beispiele finden Sie hier

Hoffe das hilft :)

0
Hem Shrestha

@erdna Bitte verweisen Sie auf meinen untenstehenden Code. Möglicherweise wird es für Sie hilfreich sein.

     int firstVisibleItem, visibleItemCount, totalItemCount;
   recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {

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

            visibleItemCount = layoutManager.getChildCount();
            totalItemCount = layoutManager.getItemCount();
            firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
            Log.e("firstVisibleItem", firstVisibleItem + "");
            Log.e("visibleItemCount", visibleItemCount + "");
            Log.e("totalItemCount", totalItemCount + "");

            if (page != total_page_index) {
                if (loading) {
                    if ((visibleItemCount + firstVisibleItem) >= totalItemCount) {
                        Log.e("page", String.valueOf(page));
                        page=page+1;
                        new GetSummary().execute(String.valueOf(page), "");
                        loading = false;
                    }
                }
            }

        }


    });
0
komal akhani

Ich lasse dir meine Näherung. Funktioniert gut für mich.

Ich hoffe es hilft dir.

/**
 * 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
Dani

Ich habe es gerade ausprobiert:

    final LinearLayoutManager rvLayoutManager = new LinearLayoutManager(this);
    rvMovieList = (RecyclerView) findViewById(R.id.rvMovieList);
    rvMovieList.setLayoutManager(rvLayoutManager);
    adapter = new MovieRecyclerAdapter(this, movies);
    rvMovieList.setAdapter(adapter);
    adapter.notifyDataSetChanged();

    rvMovieList.addOnScrollListener(new OnScrollListener() {
        boolean loading = true;
        int pastVisiblesItems, visibleItemCount, totalItemCount;
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            visibleItemCount = rvLayoutManager.getChildCount();
            totalItemCount = rvLayoutManager.getItemCount();
            pastVisiblesItems = rvLayoutManager.findFirstVisibleItemPosition();

            int lastitem = visibleItemCount + pastVisiblesItems;

            if ( lastitem == totalItemCount
                    && page < total_pages
                    && !keyword.equals("") ) {
                // hit bottom ..

                String strnext;
                if (page == total_pages - 1) {
                    int last = total_results - (page * 20);
                    strnext = String.format(getResources().getString(R.string.next), last);
                } else {
                    strnext = String.format(getResources().getString(R.string.next), 20);
                }
                btNext.setText(strnext);
                btNext.setVisibility(View.VISIBLE);
            } else {
                btNext.setVisibility(View.INVISIBLE);
            }

            if(pastVisiblesItems == 0 &&  page > 1){
                // hit top
                btPrev.setVisibility(View.VISIBLE);
            } else {
                btPrev.setVisibility(View.INVISIBLE);
            }
        }
    });

Ich habe verwirrt, wo loading mit true oder false abgelegt wird. Ich versuche, es zu entfernen, und dieser Code funktioniert wie erwartet.

0
Sofyan Thayf
if (layoutManager.findLastCompletelyVisibleItemPosition() == 
 recyclerAdapter.getItemCount() - 1) {
    //load more items.
 }

Fair und einfach ... Das wird funktionieren.

0
GvSharma

Überprüfen Sie, ob alles genau erklärt wird: Paginierung mit RecyclerView Von A bis Z

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

in 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);
    }
}
0
awsleiman
 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 
                        }
                    }          
                }
            });  
0
Anirudh