Как реализовать бесконечный список с RecyclerView?

346

Я хотел бы изменить ListViewна RecyclerView. Я хочу использовать onScrollиз OnScrollListenerвRecyclerView , чтобы определить , является ли пользователь прокручивается до конца списка.

Как узнать, прокручивает ли пользователь конец списка, чтобы я мог получить новые данные из службы REST?

erdna
источник
Пожалуйста, проверьте эту ссылку Это поможет вам .. stackoverflow.com/questions/26293461/…
samsad
32
Пожалуйста, внимательно прочитайте вопрос! Я знаю, как сделать это с помощью ListView, но НЕ как реализовать это с помощью RecycleView .
Эрдна
как реализовать loadmore onscrolllistener в android.data загружен, но обновлен на существующих данных, пожалуйста, помогите мне
Harsha
2
Вы можете использовать эту библиотеку: github.com/rockerhieu/rv-adapter-endless . Он основан на идее cwac-endlessдля ListView.
Хиеу Рокер

Ответы:

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
46
Всем, кто findFirstVisibleItemPosition()не решил, вы должны перейти RecyclerView.LayoutManagerнаLinearLayoutManager
Амр Мухаммед
27
где условие сделать loading = trueснова?
Бхаргав
11
Не забудьте добавить loading = trueпосле //Do paginationчасти. в противном случае этот код будет выполняться только один раз, и вы не сможете загрузить больше элементов.
Шайшав Джогани
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()и предупреждение исчезнет! Вы можете прочитать больше из этого ТАКОГО вопроса .

Поскольку 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();.
Цзин Ли
3
Поместите код в метод onScrollStateChanged () вместо метода onScrolled ().
Анирудх
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.

Хай чжан
источник
Это отличный и простой ответ! Спасибо!
Андраник
2
Аналогичное решение можно использовать для обновления при обновлении, проверив! RecyclerView.canScrollVertically (-1).
LordParsley
Лучшее решение там. Спасибо приятель!
Angmar
1
@LordParsley Спасибо, обновлено. Это также один из способов, которым SwipeRefreshLayout проверяет наличие обновлений по запросу.
Хай Чжан
1
@EpicPandaForce Если вы используете, RecyclerViewвам снова повезет, потому что вы можете просто сделать статическую копию, View.canScrollVertically()поскольку связанные методы помечены как открытые, а не как защищенные RecyclerView. Вы также можете расширить, RecyclerViewчтобы повторно реализовать, canScrollVertically()если хотите. Третий и простой способ - позвонить recyclerView.getLayoutManager().canScrollVertically(). Отредактировано в моем ответе.
Хай Чжан
67

Вот другой подход. Это будет работать с любым менеджером макета.

  1. Сделать класс адаптера абстрактным
  2. Затем создайте абстрактный метод в классе адаптера (например, load ())
  3. В onBindViewHolder проверьте позицию if last и вызовите load ()
  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 ..
Самер
1
@mcwise добавить логическое значение Finished = false; когда вы достигнете конца, make done = true .. измените текущее условие if на if ((position> = getItemCount () - 1) && (!
done
1
Это должно быть помечено как правильный ответ. Это элегантно и просто.
Грег Эннис
2
Хороший пример превосходного решения, похороненного под популярностью первого решения. Очень часто последняя строка будет привязана к отдельному типу представления, который служит баннером «Загрузка ...». Когда этот тип представления получает вызов связывания, он устанавливает обратный вызов, который запускает уведомление об изменении данных, когда доступно больше данных. Принятый ответ на подключение слушателя прокрутки является расточительным и, вероятно, не работает для других типов макетов.
Майкл Хейс
3
@mcwise, он будет бесконечно звонить, только если пользователь активно прокручивает и возвращается к последнему элементу. onBindViewHolder вызывается только один раз каждый раз, когда представление выходит на экран и не будет постоянно вызывать его.
Дэвид Лю
18

Мой ответ - модифицированная версия Noor. Я перешел от того, ListViewгде у меня было EndlessScrollListener(что вы можете легко найти во многих ответах на 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
Ваша реализация требует RecyclerView для использования LinearLeayoutManager. Плохая идея!
Орри
@ Орри Не могли бы вы объяснить, почему? Спасибо!
Федерико Понци
1
В вашем onScrolled вы приводите LayoutManager из RecyclerView в LinearLayoutManager. Это не работает для StaggredGridLayout или Anthing еще. В StaggredGridLayout нет функции findFirstVisibleItemPosition ().
Орри
@Orri Спасибо за указание на это! Но я не думаю, что это такая плохая идея, так как она сработала для меня и, исходя из голосов, других дел :)
Федерико Понци
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
пожалуйста, дайте какое-нибудь решение для повторного просмотра с помощью загрузки номера страницы с Android, передаваемой Android
Harsha
getWebServiceData (); mStoresAdapterData.setOnLoadMoreListener (new StoresAdapter.OnLoadMoreListener () {@Override public void onLoadMore () {// добавьте ноль, поэтому адаптер будет проверять view_type и показывать индикатор выполнения в нижнем хранилище storeList.add (ноль); mStoresnoizeStistInserserserserDataSterizeInserserData). () - 1); ++ pageNumber; getWebServiceData ();}}); и называя один за другим , наконец , 2 - й страницы данных только visibled пожалуйста помощь
Harsha
Я перепробовал многие варианты разбивки на страницы recyclerView, но ни одна из них не сработала, кроме этой, у вас есть мой голос сэра
Мохаммед Элрашид
9

Большинство ответов предполагают RecyclerViewиспользование LinearLayoutManager, или GridLayoutManager, или даже StaggeredGridLayoutManager, или предполагают, что прокрутка является вертикальной или горизонтальной, но никто не опубликовал полностью общий ответ .

Использование ViewHolderадаптера - явно не хорошее решение. Адаптер может использовать более одного RecyclerView. Это "адаптирует" их содержание. Это должен быть Recycler View (это тот класс, который отвечает за то, что в данный момент отображается пользователю, а не адаптер, который отвечает только за предоставление контента 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.
    }
}
Yairopro
источник
1
Ваш ответ правильный, и он работает на все, LayoutManagersно только на один момент: переместите код расчета в другой метод scrollListener - onScrollStateChangedвместо этого переместите его в onScrolled. И в onScrollStateChangedтолько что проверьте:if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer
Спасибо за ваш совет, но я не думаю, что это будет лучшим решением. Разработчик хочет, чтобы его слушатель был запущен даже до того, как просмотрщик перестанет прокручивать. Таким образом, он может завершить загрузку новых элементов данных (если данные хранятся локально), или даже добавить держатель представления индикатора выполнения в конце просмотра реселлера, поэтому, когда просмотр рециркулятора прекратит прокручиваться, он может идти по кругу. -бар как последний видимый элемент.
Yairopro
6

Хотя принятый ответ работает отлично, в приведенном ниже решении используется addOnScrollListener, поскольку setOnScrollListener устарела и уменьшает количество переменных и условия.

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();
            }
        }
    }
});
capt.swag
источник
1
Иногда мой метод loadMore () трижды вызывался в onScrolled (). Любая идея, почему он вызывается трижды, и как прекратить вызывать его несколько раз.
вед
dy> 0 всегда ложно
Душянт Сутар
@ved Это потому, что у прокрутки есть 3 состояния: прокрутка, прокрутка, бездействие. Вы выбираете тот, который подходит.
TheRealChx101
5

Хотя ответов на этот вопрос так много, я хотел бы поделиться нашим опытом создания бесконечного представления списка. Недавно мы внедрили собственный Карусельный 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
}

Это решение основано на ответе Кушала Шармы. Тем не менее, это немного лучше, потому что:

  1. Он использует onScrollStateChangedвместо onScroll. Это лучше, потому что onScrollвызывается каждый раз, когда есть какое-либо движение в RecyclerView, тогда как onScrollStateChangedвызывается только тогда, когда состояние RecyclerView изменяется. С помощьюonScrollStateChanged сэкономит вам процессорное время и, как следствие, батарею.
  2. Поскольку здесь используются функции расширения, это можно использовать в любом имеющемся у вас RecyclerView. Код клиента всего 1 строка.
Tiago
источник
Когда я это сделал Потяните SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working закрытый внутренний класс ! .isRefreshing = false}}
Навин Кумар М
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

Хорошо, я сделал это с помощью метода onBindViewHolder RecyclerView.Adapter.

адаптер:

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;
}
erdna
источник
1
полагаться на onBind не очень хорошая идея. RecyclerView делает кэширование представлений + имеет некоторые менеджеры компоновки, имеет средства для предварительного кэширования вещей. Я бы предложил установить прослушиватель с прокруткой, и, когда прослушиватель с прокруткой останавливается, получите последнюю видимую позицию элемента в менеджере раскладки. Если вы используете менеджеры по умолчанию, все они имеют методы findLastVisible **.
igit
@yigit Как далеко RecyclerView будет пытаться перехитрить вещи? Если у вас есть 500 предметов, я действительно сомневаюсь, что они будут кэшироваться так далеко (в противном случае это будет просто гигантская трата производительности). Когда он действительно начнет кэшироваться (даже когда у нас элемент 480), это означает, что пришло время загрузить другой пакет в любом случае.
Алексей Орлов
1
Он не сопрягает вещи с предварительной кэш-памятью, если не выполнять плавную прокрутку до далекой позиции. В этом случае LLM (по умолчанию) размещает две страницы вместо одной, чтобы он мог найти целевое представление и замедлить его. Это делает кеш-представления, которые выходят за пределы. По умолчанию этот размер кэша равен 2, и его можно настроить с помощью setItemCacheSize.
igit
1
@yigit, просто обдумывая проблему, я не понимаю, как кеширование может привести к проблеме здесь; нас интересует только первый раз, когда «последние» пункты связаны так или иначе! Пропуск вызова onBindViewHolder, когда менеджер компоновки повторно использует кэшированное представление, которое уже было связано, - это просто не проблема, когда мы заинтересованы в подкачке дополнительных данных в конце списка. Верный?
Грег Эннис
2
В моей библиотеке FlexibleAdapter я выбрал бесконечную прокрутку при входящем звонке onBindViewHolder(), она работает как шарм. Реализация крошечная по сравнению со слушателем прокрутки и проще в использовании.
Давидеас
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) {}
}
walkingice
источник
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 
                        }
                    }          
                }
            });  
Anirudh
источник
1
onScrollStateChanged()не будет работать, так как он будет вызываться только при изменении состояния прокрутки, а не при прокрутке. Вы должны использовать onScrolled()метод.
Мрадзински
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 и добавление новых результатов в адаптер.

Я могу опубликовать более полный пример, если это не ясно?

speedynomads
источник
Я знаю, что этот пост действительно старый, но я новичок и не знаю, где взять переменные, которые вы сравниваете. Я полагаю, что 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 промежутков
Деннис Зинковски
recyclerView.canScrollVertically (1) сделает это, но для этого требуется API 14. :(
Sudheesh Mohan
2

Существует легкая в использовании библиотека для этого paginate . Поддерживает как ListView, так и RecyclerView (LinearLayout, GridLayout и StaggeredGridLayout).

Вот ссылка на проект на Github

nonybrighto
источник
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

toobsco42
источник
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();
                }
            });
        }
    }
}
FRL
источник
1

@kushal @abdulaziz

Почему бы не использовать эту логику вместо этого?

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;
        }
      }
    }
  }
leonapse
источник
Мой onScroller () трижды вызывал один элемент, поэтому я получаю трижды end_of_list и мою логику нумерации страниц трижды. Как исправить это, чтобы вызвать нумерацию страниц только один раз.
Вед
@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

Я создал LoadMoreRecyclerView, используя Abdulaziz Noor Ответ

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

// Добавить его в 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();
    }
}
Zar E Ahmer
источник
1

Также возможно реализовать без прослушивателя прокрутки, используя только чистую логику модели данных. Представление прокрутки требует, чтобы получить элементы по позиции, а также максимальное количество элементов. Модель может иметь фоновую логику для выборки нужных элементов по частям, а не по одному, и делать это в фоновом потоке, уведомляя представление о готовности данных.

Этот подход позволяет иметь очередь выборки, которая предпочитает самые последние запрашиваемые (так видимые в настоящее время) элементы по сравнению со старыми (вероятнее всего, уже прокрученными) представлениями, контролировать количество используемых параллельных потоков и тому подобное. Полный исходный код для этого подхода (демонстрационное приложение и многоразовая библиотека) доступен здесь .

Аудрюс Мескаускас
источник
0

Есть метод public void setOnScrollListener (RecyclerView.OnScrollListener listener)в https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29 . Используйте это

РЕДАКТИРОВАТЬ:

Переопределите 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, чтобы определить, что пользователь прокрутил до конца списка?
Эрдна
В RecyclerView нет функции onScroll.OnScrollListener ! Вы смешиваете AbsListView.OnScrollListener с RecyclerView.OnScrollListener.
Эрдна
да, это должно бытьonScrollStateChanged
Пантера
onScrollStateChangedне будет работать, так как он будет вызываться только при изменении состояния прокрутки, а не при прокрутке.
Педро Оливейра
@PedroOliveira Можно ли с помощью onScrollStateChangedопределить, что пользователь прокручивает до последнего элемента?
Эрдна
0

Проверьте это, все объясняется подробно: разбиение на страницы с помощью 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);
    }
}
awsleiman
источник
Ссылка может содержать ответ, но попробуйте объяснить некоторые существенные части объяснений в ссылке в вашем посте.
Ян
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);
Гедрюс Шликас
источник