Як реалізувати нескінченний список за допомогою RecyclerView?


346

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

Як дізнатись, якщо користувач прокручує до кінця списку, щоб я міг отримати нові дані з послуги REST?


Будь ласка , перевірте це посилання Це допоможе вам .. stackoverflow.com/questions/26293461 / ...
samsad

32
Будь ласка, уважно прочитайте питання! Я знаю, як це зробити за допомогою ListView, але НЕ, як реалізувати це за допомогою RecycleView .
erdna

як реалізувати loadmore onscrolllistener в android.data завантажується, але оновлено наявні дані, будь ласка, допоможіть мені
Харша

2
Ви можете використовувати цю бібліотеку: github.com/rockerhieu/rv-adapter-endless . Він заснований на ідеї cwac-endlessдля ListView.
Hieu Rocker

Відповіді:


422

Завдяки @Kushal і ось як я його реалізував

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

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

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

Не забудьте додати

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

11
Що я можу зробитиStaggeredGridLayoutManager
Пратік Бутані

16
А як щодоmLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
laaptu

45
Для всіх, хто findFirstVisibleItemPosition()не вирішив вас, слід змінити RecyclerView.LayoutManagerнаLinearLayoutManager
Амр Мухаммед

26
де умова зробити loading = trueзнову?
Бхаргав

10
Не забудьте додати 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 () замість методу onScroll ().
Аніруд

2
@ toobsco42 Ви можете скористатися цим:int[] firstVisibleItemPositions = new int[yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
MathieuMaree

1
Що це за знахідка FirstVisibleItemPosition () не вирішено
Suresh Sharma

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
Подібне рішення можна використовувати для поновлення для оновлення, перевіривши! RecilerView.canScrollVerically (-1).
LordParsley

Найкраще рішення там. Дякую, чувак!
Ангмар

1
@LordParsley Дякую, оновлено. Це також один із способів SwipeRefreshLayout перевіряє наявність "pull" для оновлення.
Хай Чжан

1
@EpicPandaForce Якщо ви користуєтесь, RecyclerViewвам знову пощастить, тому що ви можете просто зробити статичну копію, View.canScrollVertically()оскільки пов'язані методи позначені загальнодоступними, а не захищеними RecyclerView. Ви також можете продовжити RecyclerViewповторну реалізацію, canScrollVertically()якщо хочете. Третій і простий спосіб - зателефонувати recyclerView.getLayoutManager().canScrollVertically(). Відредаговано у моїй відповіді.
Хай Чжан

67

Ось ще один підхід. Він буде працювати з будь-яким менеджером компонування.

  1. Зробіть клас адаптера абстрактним
  2. Потім створіть абстрактний метод у класі адаптера (наприклад, load ())
  3. У onBindViewHolder перевірте позицію, якщо останній та завантажений виклик ()
  4. Перевизначте функцію load () під час створення об'єкта адаптера у вашій діяльності чи фрагменті.
  5. За допомогою функції перенавантаженого навантаження реалізуйте свій дзвінок більшого навантаження

Для детального розуміння я написав допис у блозі та приклад проекту знайдіть його тут 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 додати булеве закінчення = false; коли ви досягнете кінця, зробіть готовим = true. Змініть поточний, якщо умова, на if ((позиція> = getItemCount () - 1) && (! закінчено))
Sabeer Mohammed

1
Це слід позначити як правильну відповідь. Це елегантно і просто.
Грег Енніс

2
Хороший приклад вищого рішення, похованого під популярністю першого рішення. Дуже часто останній ряд буде прив’язаний до окремого виду перегляду, який виконує функції банера "Завантаження ...". Коли цей тип перегляду отримує прив'язку дзвінка, він встановлює зворотний дзвінок, який запускає сповіщення про зміну даних, коли доступно більше даних. Прийнята відповідь підключення слухача прокрутки є марною і, ймовірно, не працює для інших типів компонування.
Майкл Хейс

3
@mcwise він буде нескінченно дзвонити, лише якщо користувач активно прокручує назад і повертається до останнього елемента. onBindViewHolder викликається лише один раз кожного разу, коли подання виходить на екран, і не буде постійно його викликати.
Девід Лю

18

Моя відповідь - це модифікована версія Noor. Я перейшов від місця, ListViewде у мене було EndlessScrollListener(що ви можете легко знайти у багатьох відповідях на ТАК), щоб 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. Погана ідея!
Оррі

@Orri Чи можете ви поясніть, чому? Дякую!
Федеріко Понці

1
У програмі onScroll ви передаєте LayoutManager з RecyclerView в LinearLayoutManager. Він не працює для StaggredGridLayout або anthing. У StaggredGridLayout не знайденоFirstVisibleItemPosition ().
Оррі

@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 потрібно змінити в кінці асинхронних завдань. Сподіваюся, це буде корисно!


Я знайшов цю відповідь після декількох днів допитувань себе, в основному. Це найкраще рішення, imo.
wsgeorge

будь ласка, дайте рішення для переробки рециркуляторів із завантаженням більшого номера сторінки на Android
Харша,

getWebServiceData (); mStoresAdapterData.setOnLoadMoreListener (новий StoresAdapter.OnLoadMoreListener () {@Override public void onLoadMore () {// додати null, тому адаптер перевірить view_type і покаже панель прогресу в нижній сховищіList.add (null); () - 1); ++ pageNumber; getWebServiceData ();}}); обидва дзвонять одна за одною, нарешті, дані лише на 2-й сторінці видно, будь ласка, допоможіть
Харша,

Я спробував багато реалізації сторінки розробки recilerView, але жодна з них не спрацювала, крім цієї, у вас є мій актуальний сер
Мохаммед Елрашієд

9

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

Використання ViewHolderадаптера 'явно не є вдалим рішенням. Можливо, у адаптера використовується більше 1 RecyclerView. Він "адаптує" їх зміст. Це повинно бути Ресайклер 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.
    }
}

1
Ваша відповідь правильна, і вона працює для всіх, LayoutManagersале лише один момент: перемістіть свій розрахунковий код в інший метод scrollListener - перемістіть його onScrollStateChangedнатомість onScrolled. І onScrollStateChangedлише перевіримо:if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer

Дякую за пораду, але я не думаю, що це було б кращим рішенням. Розробник хоче, щоб його слухач був запущений ще до того, як перегляд рециркулятора перестане прокручуватися. Таким чином, він може закінчити завантаження нових елементів даних (якщо дані зберігаються локально), або навіть додати утримувач перегляду панелі прогресу в кінці рециркулятора, тож коли рециркулятор перестане прокручуватися, він може пройти по колу -bar як останній видимий елемент.
Яїпропро

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

1
Іноді мій метод loadMore () називається тричі в onScroll (), будь-яка ідея, чому його називають тричі і як перестати називати його кілька разів.
Вед

dy> 0 завжди помилково
Dushyant Suthar

@ved Це тому, що прокрутка має 3 стани: прокрутка, прокрутка, неробота. Ви вибираєте відповідний.
TheRealChx101

5

Хоча відповідей на запитання так багато, я хотів би поділитися нашим досвідом створення нескінченного перегляду списку. Нещодавно ми впровадили користувальницький карусель LayoutManager, який може працювати в циклі, прокручуючи список нескінченно, а також до певного моменту. Ось детальний опис на GitHub .

Я пропоную вам ознайомитись із цією статтею з короткими, але цінними рекомендаціями щодо створення користувальницьких менеджерів компонування: 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 рядок.

Коли я здійснив SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working приватний внутрішній клас ! .isRefreshing = false}}
Naveen Kumar M

5

Ось як я це роблю, просто і коротко:

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

            }
        }
    });

3

Гаразд, я зробив це за допомогою методу 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;
}

1
покладатися на onBind - не дуже гарна ідея. RecyclerView робить кеш-вигляд + має деякі менеджери компонування має засоби попереднього кешування речей. Я б запропонував встановити прослуховувач прокрутки, і коли слухач прокрутки зупиниться, отримайте останню видиму позицію елемента у менеджера макетів. Якщо ви використовуєте менеджери макетів за замовчуванням, всі вони мають методи findLastVisible **.
yigit

@yigit Наскільки далеко RecyclerView намагатиметься передбачити речі? Якщо у вас є 500 предметів, я дуже сумніваюся, що це буде кешувати так далеко (інакше це буде просто гігантська трата продуктивності). Коли він насправді почне кешувати (навіть коли ми в 480 елементах), це означає, що пора все-таки завантажити іншу партію.
Олексій Орлов

1
Це соплі попередньо кешувати речі, якщо це не є плавною прокруткою до далекої позиції. У такому випадку макет LLM (за замовчуванням) розміщує дві сторінки замість однієї, щоб він міг знайти цільовий вигляд і зменшити його. Він робить кешові види, які виходять за межі. За замовчуванням цей розмір кешу становить 2 і може бути налаштований через setItemCacheSize.
yigit

1
@yigit просто роздумуючи над проблемою, я не бачу, як кешування може поставити тут проблему; нас цікавить лише перший раз, коли "останні" пункти пов'язані все одно! Відсутній виклик на onBindViewHolder, коли менеджер макетів повторно використовує кешоване подання, яке вже було пов'язане, просто не є проблемою, коли нам цікаво підключити більше даних у кінці списку. Правильно?
Грег Енніс

2
У моїй бібліотеці FlexibleAdapter я вирішив прийняти нескінченну прокрутку під час виклику onBindViewHolder(), це працює як шарм. Реалізація є невеликою порівняно зі слухачем прокрутки, і її легше використовувати.
Davideas

3

Мій спосіб виявити подію завантаження - не виявити прокрутку, а прослухати, чи було додано останнє представлення. Якщо останній перегляд був доданий, я вважаю його тимчасовим, щоб завантажити більше вмісту.

class MyListener implements RecyclerView.OnChildAttachStateChangeListener {
    RecyclerView mRecyclerView;

    MyListener(RecyclerView view) {
        mRecyclerView = view;
    }

    @Override
    public void onChildViewAttachedToWindow(View view) {

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

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

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

2

Я б спробував розширити використаний LayoutManager(наприклад LinearLayoutManager) і scrollVerticallyBy()метод переопределення . По-перше, я б зателефонував superспочатку, а потім перевірив повернене ціле значення. Якщо значення дорівнює 0нижній або верхній межі. Тоді я б скориставсяfindLastVisibleItemPosition() методом, щоб дізнатись, до якої межі досягнуто, та завантажити більше даних, якщо потрібно. Просто ідея.

Крім того, ви навіть можете повернути своє значення за допомогою цього методу, дозволяючи розкручувати та показувати показник "завантаження".


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

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

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

1
onScrollStateChanged()не працюватиме, оскільки він буде викликаний лише тоді, коли ви зміните стан прокрутки, а не під час прокрутки. Ви повинні використовувати onScrolled()метод.
mradzinski

2

Я досяг нескінченної реалізації типу прокрутки, використовуючи цю логіку в onBindViewHolderметоді свого RecyclerView.Adapterкласу.

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

За допомогою цього коду, коли RecyclerView потрапляє до останнього пункту, він збільшує поточну сторінку та зворотні виклики в інтерфейсі, який відповідає за завантаження більшої кількості даних з api та додавання нових результатів у адаптер.

Я можу розмістити більш повний приклад, якщо це не зрозуміло?


Я знаю, що ця публікація справді стара, але я дещо новачок і не знаю, де взяти змінні, які ви порівнюєте. mCurrentPage і mTotalPageCount переглядають ваш набір даних, я припускаю, а mItems - це масив елементів списку, але як я можу знайти позицію?
BooleanCheese

Ви можете подивитися, як я реалізував свій RecyclerView.Adapter тут: github.com/dominicthomas/FlikrGridRecyclerView/blob/master/app/…
speedynomads

2

Для людей, які використовують StaggeredGridLayoutManager ось моя реалізація, вона працює для мене.

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

        firstVivisibleItems = mLayoutManager.findFirstVisibleItemPositions(firstVivisibleItems);

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

    }

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

@SudheeshMohan Ось повний клас, це мало) У своєму проекті я змінюю динамічно кількість прольотів, і це буде працювати для 1 та 2 проміжків
Dennis Zinkovski

recilerView.canScrollVerically (1) зробить це, але для цього потрібен API 14. :(
Sudheesh Mohan,

2

Існує проста у користуванні бібліотека для цього названого пагіната . Підтримує ListView і RecyclerView (LinearLayout, GridLayout та StaggeredGridLayout).

Ось посилання на проект на Github


2
  1. Створіть абстрактний клас і розширить RecyclerView.OnScrollListener

    public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
    private int previousTotal = 0;
    private boolean loading = true;
    private int visibleThreshold;
    private int firstVisibleItem, visibleItemCount, totalItemCount;
    private RecyclerView.LayoutManager layoutManager;
    
    public EndlessRecyclerOnScrollListener(RecyclerView.LayoutManager layoutManager, int visibleThreshold) {
    this.layoutManager = layoutManager; this.visibleThreshold = visibleThreshold;
    }
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);
    
    visibleItemCount = recyclerView.getChildCount();
    totalItemCount = layoutManager.getItemCount();
    firstVisibleItem = ((LinearLayoutManager)layoutManager).findFirstVisibleItemPosition();
    
    if (loading) {
        if (totalItemCount > previousTotal) {
            loading = false;
            previousTotal = totalItemCount;
        }
    }
    if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
        onLoadMore();
        loading = true;
    }
      }
    
    public abstract void onLoadMore();}
  2. в активності (або фрагменті) додайте addOnScrollListener до recilerView

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

1

У мене є досить детальний приклад того, як потрібно страхітувати за допомогою RecyclerView. На високому рівні у мене є набір PAGE_SIZE, скажімо 30. Тому я запитую 30 предметів, а якщо я отримаю 30 назад, я запитую наступну сторінку. Якщо я отримую менше 30 елементів, я позначаю змінну, щоб вказати, що остання сторінка досягнута, і тоді я перестаю просити більше сторінок. Перевірте це і дайте мені знати, що ви думаєте.

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


1

Ось моє рішення, що використовує AsyncListUtil , в Інтернеті говорить: Зауважте, що цей клас використовує один потік для завантаження даних, тому він підходить для завантаження даних із вторинного сховища, такого як диск, але не з мережі. але я використовую odata для читання даних та непоганої роботи. Я пропускаю в своєму прикладі об'єкти даних та мережеві методи. Я включаю лише приклад адаптера.

public class AsyncPlatoAdapter extends RecyclerView.Adapter {

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

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

    }

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

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

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

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

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

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

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

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

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

1

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

Мій онScroller () називав тричі для одного елемента, тому я отримую 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 або onViewCreate

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

callYourService

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

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

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

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

1

Це також можливо реалізувати без прослуховувача прокрутки, використовуючи лише чисту логіку моделі даних. У вікні прокрутки потрібно отримати елементи за позицією, а також максимальну кількість елементів. Модель може мати фонову логіку, щоб отримати потрібні елементи в шматки, а не по черзі, і зробити це у фоновому потоці, повідомляючи про перегляд, коли дані будуть готові.

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


0

Існує метод 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, щоб визначити, що користувач прокрутив до кінця списку?
erdna

На RecyclerView.OnScrollListener немає функції onScroll ! Ви змішуєте AbsListView.OnScrollListener з RecyclerView.OnScrollListener.
erdna

ya це повинно бутиonScrollStateChanged
Panther

onScrollStateChangedне працюватиме, оскільки він буде викликаний лише тоді, коли ви зміните стан прокрутки, а не під час прокрутки.
Педро Олівейра

@PedroOliveira Чи можна onScrollStateChangedвизначити, що користувач прокручує останній елемент?
erdna

0

Перевірте це, кожна річ пояснюється детально: Пагинація за допомогою RecyclerView від A до 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();
}

у 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

Жодна з цих відповідей не враховує, якщо список занадто малий чи ні.

Ось фрагмент коду, який я використовував, який працює на 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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.