Como implementar uma lista sem fim com o RecyclerView?

346

Eu gostaria de mudar ListViewpara RecyclerView. Eu quero usar o onScrolldo OnScrollListenerno RecyclerViewpara determinar se um usuário rolada para o final da lista.

Como sei se um usuário rola para o final da lista para que eu possa buscar novos dados de um serviço REST?

erdna
fonte
Por favor, verifique este link Ele irá ajudá-lo .. stackoverflow.com/questions/26293461/…
samsad
32
Por favor, leia a pergunta com atenção! Eu sei como fazer isso com um ListView, mas NÃO como implementá-lo com um RecycleView .
erdna
como implementar loadmore onscrolllistener em android.data é carregado, mas atualizado em dados existentes por favor me ajude
Harsha
2
Você pode usar esta biblioteca: github.com/rockerhieu/rv-adapter-endless . É baseado na idéia de cwac-endlesspara ListView.
Hieu Rocker

Respostas:

422

Graças a @Kushal e foi assim que eu o implementei

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

Não esqueça de adicionar

LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
Abdulaziz Noor
fonte
11
O que posso fazer porStaggeredGridLayoutManager
Pratik Butani
16
O que sobremLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
Laaptu
45
Para todos os que findFirstVisibleItemPosition()não resolvidos você deve mudar RecyclerView.LayoutManagerparaLinearLayoutManager
Amr Mohammed
26
onde está a condição para fazer de loading = truenovo?
Bhargav
10
Não se esqueça de adicionar loading = truedepois da //Do paginationparte. caso contrário, esse código será executado apenas uma vez e você não poderá carregar mais itens.
Shaishav Jogani
165

Faça essas variáveis.

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

Defina no Scroll para visualizar a recicladora.

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

Nota: Verifique se você está usando LinearLayoutManagercomo gerenciador de layout RecyclerView.

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

e para uma grade

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

Divirta-se com seus pergaminhos sem fim !! ^. ^

Atualização: mRecyclerView. setOnScrollListener () está obsoleto, substitua por mRecyclerView.addOnScrollListener()e o aviso desapareceu! Você pode ler mais sobre esta questão SO .

Como o Android agora suporta oficialmente o Kotlin, aqui está uma atualização para o mesmo:

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

e adicione-o ao seu RecyclerView assim

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

Para um exemplo de código completo, sinta-se à vontade para consultar este repositório do Github .

Kushal Sharma
fonte
14
Alguma idéia de como aplicar esta solução com um GridLayoutManager? Como o findFirstVisibleItemPosition()método está disponível apenas com o LinearLayoutManager, não consigo descobrir como fazê-lo funcionar.
MathieuMaree
11
Informações adicionais: nesse caso, usar visibleItemCount = mLayoutManager.getChildCount();se comporta da mesma maneira que usar visibleItemCount = mRecyclerView.getChildCount();.
perfil completo de Jing Li
3
Coloque o código no método onScrollStateChanged () em vez do método onScrolled ().
Anirudh
2
@ toobsco42 Você pode usar isto:int[] firstVisibleItemPositions = new int[yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
MathieuMaree
11
O que é isso findFirstVisibleItemPosition () não resolvido
Suresh Sharma
72

Para aqueles que só querem ser notificados quando o último item é totalmente exibido, você pode usar View.canScrollVertically().

Aqui está a minha implementação:

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

Nota: Você pode usar recyclerView.getLayoutManager().canScrollVertically()se desejar oferecer suporte à API <14.

Hai Zhang
fonte
Esta é uma resposta ótima e simples! Obrigado!
Andranik
2
Uma solução semelhante pode ser usada para puxar para atualizar, verificando! RecyclerView.canScrollVertically (-1).
31415 LordParsley
Melhor solução lá fora. Valeu cara!
Angmar
11
@LordParsley Obrigado, atualizado. Essa também é uma das maneiras pelas quais o SwipeRefreshLayout verifica se o pull é atualizado.
Hai Zhang
11
@EpicPandaForce Se você estiver usando, RecyclerViewterá sorte novamente, porque você pode simplesmente fazer uma cópia estática, View.canScrollVertically()pois os métodos relacionados são marcados como públicos em vez de protegidos RecyclerView. Você também pode estender RecyclerViewpara reimplementar, canScrollVertically()se quiser. Uma terceira e simples maneira é ligar recyclerView.getLayoutManager().canScrollVertically(). Editado na minha resposta.
Hai Zhang #
67

Aqui está outra abordagem. Ele funcionará com qualquer gerenciador de layout.

  1. Tornar a classe do adaptador abstrata
  2. Em seguida, crie um método abstrato na classe do adaptador (por exemplo, load ())
  3. No onBindViewHolder, verifique a posição se última e chame load ()
  4. Substitua a função load () ao criar o objeto do adaptador em sua atividade ou fragmento.
  5. Na função carga sobrecarregada, implemente sua chamada loadmore

Para uma compreensão detalhada, escrevi uma postagem no blog e um exemplo de projeto, acesse aqui 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();
            }
        };
   }
}
Sabeer Mohammed
fonte
3
Como a solução, você não precisa tornar seu adaptador abstrato; em vez disso, você pode criar uma interface no adaptador e fazer com que sua atividade a implemente, mantendo-a flexível e elegante, por exemplo: ItemDisplayListener => onItemDisplayed (int position). Desta forma, você pode carregar em qualquer posição: a N, N-1, N-2 ..
Samer
11
@mcwise adiciona um booleano finished = false; quando você chegar ao final make terminou = true .. mudar a corrente se a condição de if ((posição> = getItemCount () - 1) && (terminado)!)
Sabeer Mohammed
11
Isso deve ser marcado como a resposta correta. Isso é elegante e simples.
Greg Ennis
2
Um bom exemplo da solução superior enterrada sob a popularidade da primeira solução. Muitas vezes, a última linha será vinculada a um tipo de exibição separado, que serve como um banner "Carregando ...". Quando esse tipo de exibição recebe uma chamada de ligação, ele define um retorno de chamada que aciona uma notificação de alteração de dados quando mais dados estão disponíveis. A resposta aceita para conectar o ouvinte de rolagem é um desperdício e provavelmente não funciona para outros tipos de layout.
Michael Hays
3
@mcwise chamaria infinitamente se o usuário rolar para longe e voltar ao último item. onBindViewHolder é chamado apenas uma vez cada vez que a visualização entra na tela e não a chamava constantemente.
David Liu
18

Minha resposta é uma versão modificada do Noor. Passei de um local ListViewonde eu tinha EndlessScrollListener(que você pode encontrar facilmente em muitas respostas no SO) para um, RecyclerViewentão eu queria EndlessRecyclScrollListenerque atualizasse facilmente meu ouvinte anterior.

Então, aqui está o código, espero que ajude:

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

}
Federico Ponzi
fonte
11
Sua implementação requer que o RecyclerView use LinearLeayoutManager. Péssima ideia!
Orri 04/07
@Orri Você poderia explicar o porquê? Obrigado!
Federico Ponzi
11
No seu onScrolled, você converte o LayoutManager do RecyclerView para o LinearLayoutManager. Não está funcionando para StaggredGridLayout ou qualquer outra coisa. Não há findFirstVisibleItemPosition () em StaggredGridLayout.
Orri 04/07
@ Orri Obrigado por apontar isso! Mas eu não acho que é uma má ideia, uma vez que trabalhou para o meu e, com base em upvotes, outros casos :)
Federico Ponzi
10

Para mim, é muito simples:

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

Cuidado com os trabalhos assíncronos: o carregamento do mL deve ser alterado no final dos trabalhos assíncronos. Espero que seja útil!

Minh Nguyen
fonte
Eu encontrei essa resposta depois de dias me questionando, basicamente. Esta é a melhor solução, imo.
wsgeorge
por favor, dê alguma solução para recyclerview com número de página loadmore passagem android
Harsha
getWebServiceData (); mStoresAdapterData.setOnLoadMoreListener (new StoresAdapter.OnLoadMoreListener () {@Override public void onLoadMore () {// adiciona nulo, para que o adaptador verifique o view_type e mostre a barra de progresso na parte inferior storesList.add (null); mStoresAdapterData.notifyItemInserted (Stores () - 1); ++ pageNumber; getWebServiceData ();}}); tanto chamar um após o outro de dados finalmente 2ª página única visibled ajuda por favor
Harsha
Eu tentei muitas implementação da paginação recyclerView mas nenhum deles funcionou, exceto este, você tem meu senhor upvote
Mohammed Elrashied
9

A maioria das respostas está assumindo os RecyclerViewusos a LinearLayoutManager, ou GridLayoutManager, ou mesmo StaggeredGridLayoutManager, ou assumindo que a rolagem é vertical ou horyzontal, mas ninguém postou uma resposta completamente genérica .

Usar o ViewHolderadaptador do claramente não é uma boa solução. Um adaptador pode ter mais de 1 RecyclerViewusando-o. Ele "adapta" seu conteúdo. Deve ser a Visualização do Reciclador (que é a única classe responsável pelo que atualmente é exibido ao usuário, e não o adaptador responsável apenas pelo fornecimento de conteúdo RecyclerView) que deve notificar seu sistema de que mais itens são necessários (para carga).

Aqui está minha solução, usando nada além das classes abstraídas do RecyclerView (RecycerView.LayoutManager e 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
fonte
11
Sua resposta é certo e funcionou para todos LayoutManagers, mas apenas um momento: mover o código de cálculo no outro método scrollListener - movê-lo para onScrollStateChangedem vez onScrolled. E em onScrollStateChangedapenas verificar:if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer 16/02
Obrigado pelo seu conselho, mas não acho que seria uma solução melhor. O desenvolvedor deseja que seu ouvinte seja acionado mesmo antes da visualização da reciclagem parar de rolar. Portanto, pode terminar de carregar novos itens de dados (se os dados estiverem armazenados localmente) ou até mesmo adicionar um detentor da visualização da barra de progresso no final da visualização da reciclagem, para que, quando a visualização da parada parar para rolar, ela possa estar em um círculo. -bar como último item visível.
Yairopro
6

Embora a resposta aceita funcione perfeitamente, a solução abaixo usa addOnScrollListener, pois setOnScrollListener foi descontinuado e reduz o número de variáveis ​​e as condições if.

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
fonte
11
Às vezes, meu método loadMore () chama três vezes em onScrolled (). Alguma idéia de por que é chamado três vezes e como parar de chamá-lo várias vezes.
ved
dy> 0 é sempre falsa
Dushyant Suthar
@ved Isso ocorre porque a rolagem tem três estados: rolagem, rolagem, ocioso. Você escolhe um que seja adequado.
TheRealChx101
5

Embora haja muitas respostas para a pergunta, gostaria de compartilhar nossa experiência de criação da exibição de lista sem fim. Recentemente, implementamos o Carousel LayoutManager personalizado que pode funcionar no ciclo rolando a lista infinitamente e até um determinado ponto. Aqui está uma descrição detalhada no GitHub .

Sugiro que você dê uma olhada neste artigo com recomendações curtas, mas valiosas, sobre como criar LayoutManagers personalizados: http://cases.azoft.com/create-custom-layoutmanager-android/

Vladimir Tchernitski
fonte
5

Com o poder das funções de extensão do Kotlin, o código pode parecer muito mais elegante. Coloque isso em qualquer lugar que você quiser (eu o tenho dentro de um arquivo 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
                }
            }
        }
    })
}

E então use-o assim:

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

Esta solução é baseada na resposta de Kushal Sharma. No entanto, isso é um pouco melhor porque:

  1. Ele usa em onScrollStateChangedvez de onScroll. Isso é melhor porque onScrollé chamado sempre que existe qualquer tipo de movimento no RecyclerView, enquanto onScrollStateChangedé chamado somente quando o estado do RecyclerView é alterado. O uso onScrollStateChangedpoupará tempo de CPU e, consequentemente, bateria.
  2. Como isso usa funções de extensão, isso pode ser usado em qualquer RecyclerView que você possui. O código do cliente é apenas 1 linha.
Tiago
fonte
Quando eu SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working puxei a classe interna privada PullToRefresh: SwipeRefreshLayout.OnRefreshListener {substitui diversão onRefresh () {pbViewMore !!. Visible = View.GONE pageCount = 0 courseList.clear () // chamando asynctask aqui srlNoMessageRefreshLayout !!. IsRefreshing = false swipe! ! .isRefreshing = false}}
Naveen Kumar M
5

É assim que eu faço, simples e curto:

    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

            }
        }
    });
John T
fonte
3

OK, eu fiz isso usando o método onBindViewHolder de RecyclerView.Adapter.

Adaptador:

public interface OnViewHolderListener {
    void onRequestedLastItem();
}

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

    ...

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

Fragmento (ou Atividade) :

@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
fonte
11
confiar no onBind não é uma boa ideia. O RecyclerView faz cache de visualizações + tem alguns gerenciadores de layout que têm meios de pré-armazenar em cache as coisas. Sugiro definir um ouvinte de rolagem e, quando o ouvinte de rolagem parar, obter a última posição visível do item no gerenciador de layout. Se você estiver usando gerenciadores de layout padrão, todos eles terão os métodos findLastVisible **.
yigit
@yigit A que distância o RecyclerView tentará precache as coisas? Se você tiver 500 itens, duvido muito que ele fique em cache tão longe (caso contrário, será apenas um enorme desperdício de desempenho). Quando ele realmente começar a armazenar em cache (mesmo quando estivermos no elemento 480), significa que é hora de carregar outro lote de qualquer maneira.
Alex Orlov
11
Ele faz snot preCache coisas a menos que seja rolagem suave para uma posição distante. Nesse caso, o layout do LLM (por padrão) exibe duas páginas em vez de uma para que ele possa encontrar a vista de destino e desacelerar para ela. Faz visualizações de cache que ficam fora dos limites. Por padrão, esse tamanho de cache é 2 e pode ser configurado via setItemCacheSize.
yigit 28/01
11
@ yigit apenas pensando no problema, não vejo como o cache pode apresentar um problema aqui; só estamos interessados ​​na primeira vez que os "últimos" itens são vinculados de qualquer maneira! Perder a chamada para onBindViewHolder quando o gerenciador de layout reutiliza uma exibição em cache que já estava vinculada simplesmente não é um problema quando estamos interessados ​​em paginar mais dados no final da lista. Corrigir?
Greg Ennis
2
Na minha biblioteca do FlexibleAdapter, escolhi adotar o rolo infinito com a chamada onBindViewHolder(), funciona como um encanto. A implementação é pequena se comparada ao ouvinte de rolagem e é mais fácil de usar.
Davideas 25/07/16
3

Minha maneira de detectar o evento de carregamento não é detectar a rolagem, mas ouvir se a última visualização foi anexada. Se a última visualização foi anexada, considero o momento de carregar mais conteúdo.

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
fonte
2

Eu tentaria estender o método usado LayoutManager(por exemplo LinearLayoutManager) e substituir scrollVerticallyBy(). Em primeiro lugar, eu ligaria superprimeiro e depois verificaria o valor inteiro retornado. Se o valor for igual a 0, uma borda inferior ou superior será atingida. Então eu usaria o findLastVisibleItemPosition()método para descobrir qual borda é atingida e carregar mais dados, se necessário. Apenas uma ideia.

Além disso, você pode até retornar seu valor desse método, permitindo o excesso de rolagem e exibindo o indicador "loading".

sergej shafarenka
fonte
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
fonte
11
onScrollStateChanged()não funcionará, pois será chamado apenas quando você alterar o estado da rolagem e não enquanto estiver rolando. Você deve usar o onScrolled()método
precisa saber é o seguinte
2

Consegui uma implementação de tipo de rolagem infinita usando essa lógica no onBindViewHoldermétodo da minha RecyclerView.Adapterclasse.

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

Com esse código, quando o RecyclerView chega ao último item, ele incrementa a página atual e os retornos de chamada em uma interface responsável por carregar mais dados da API e adicionar os novos resultados ao adaptador.

Posso postar um exemplo mais completo se isso não estiver claro?

speedynomads
fonte
Eu sei que este post é muito antigo, mas sou um pouco novato e não sei onde obter as variáveis ​​que você está comparando. mCurrentPage e mTotalPageCount estão se referindo ao seu conjunto de dados, presumo, e mItems é a lista de matriz dos itens da lista, mas como localizo a posição?
BooleanCheese
Você pode ver como eu implementei meu RecyclerView.Adapter aqui: github.com/dominicthomas/FlikrGridRecyclerView/blob/master/app/…
speedynomads
2

Para as pessoas que usam StaggeredGridLayoutManager, aqui está minha implementação, ela funciona para mim.

 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;
    }
}
Dennis Zinkovski
fonte
@SudheeshMohan Aqui é classe cheia, é pequeno) No meu projeto eu mudança contagem de tempo de forma dinâmica, e vai trabalhos para 1 e 2 contagens de extensão
Dennis Zinkovski
recyclerView.canScrollVertically (1) fará isso, mas requer a API 14. :(
Sudheesh Mohan
2

Existe uma biblioteca fácil de usar para esse paginado nomeado . Suporta ListView e RecyclerView (LinearLayout, GridLayout e StaggeredGridLayout).

Aqui está o link para o projeto no Github

nonybrighto
fonte
2
  1. Crie uma classe abstrata e estenda 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. em atividade (ou fragmento) adicione addOnScrollListener ao recyclerView

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

Eu tenho um exemplo bastante detalhado de como paginar com um RecyclerView. Em um nível alto, eu tenho um conjunto PAGE_SIZE, digamos 30. Portanto, solicito 30 itens e, se eu receber 30 de volta, solicito a próxima página. Se eu receber menos de 30 itens, sinalizo uma variável para indicar que a última página foi alcançada e paro de solicitar mais páginas. Confira e deixe-me saber o que você pensa.

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

toobsco42
fonte
1

Aqui, minha solução usando AsyncListUtil , na Web, diz: Observe que essa classe usa um único thread para carregar os dados, portanto, é adequado para carregar dados do armazenamento secundário, como disco, mas não da rede. mas estou usando o odata para ler os dados e funcionar bem. Sinto falta do meu exemplo de entidades de dados e métodos de rede. Eu incluo apenas o adaptador de exemplo.

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
fonte
1

@kushal @abdulaziz

Por que não usar essa lógica?

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
fonte
Meu onScroller () chamou três vezes para um único item, então eu recebi end_of_list três vezes e minha lógica de paginação chamou três vezes. Como corrigir isso para chamar a paginação apenas uma vez.
ved
@ved Isso não deveria acontecer. Esse código acima garante aend_of_list condição será chamada apenas uma vez. O sinalizador _isLastItemé uma variável global na atividade que possui um valor padrão de false. O que fiz foi executar uma tarefa em segundo plano após detectar o final da lista, recuperar a próxima página e notificar o adaptador do novo conjunto de dados e, finalmente, definindo _isLastItemcomo falso novamente.
Leonapse
1

Tente abaixo:

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

}
shridutt kothari
fonte
1

Eu criei o LoadMoreRecyclerView usando Abdulaziz Noor Resposta

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

// Adicione no xml como

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

OnCreate ou 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
fonte
1

Também é possível implementar sem o ouvinte de rolagem, usando apenas a lógica pura do modelo de dados. A visualização de rolagem requer a obtenção de itens por posição, bem como a contagem máxima de itens. O modelo pode ter a lógica de segundo plano para buscar os itens necessários em blocos, em vez de um por um, e fazer isso no encadeamento em segundo plano, notificando a visualização quando os dados estiverem prontos.

Essa abordagem permite que a fila de busca que prefere os itens solicitados mais recentemente (tão visíveis no momento) aos envios mais antigos (provavelmente já rolados para longe), controle o número de threads paralelos a serem usados ​​e coisas assim. O código fonte completo dessa abordagem (aplicativo demo e biblioteca reutilizável) está disponível aqui .

Audrius Meskauskas
fonte
0

Existe um método public void setOnScrollListener (RecyclerView.OnScrollListener listener)em https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29 . Use isso

EDITAR:

Substituir onScrollStateChangedmétodo dentro do onScrollListenere faça isso

            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
            }
Pantera
fonte
11
Por favor seja mais específico! Como uso RecyclerView.OnScrollListener para determinar se o usuário rolou até o final da lista?
erdna
Não há onScroll no RecyclerView.OnScrollListener ! Você mistura o AbsListView.OnScrollListener com o RecyclerView.OnScrollListener.
erdna
ya é suposto seronScrollStateChanged
Pantera
onScrollStateChangednão funcionará, pois será chamado apenas quando você alterar o estado da rolagem e não enquanto estiver rolando.
Pedro Oliveira
@PedroOliveira É possível onScrollStateChangeddeterminar se o usuário rola para o último item?
erdna
0

Verifique se tudo está explicado em detalhes: Paginação usando o RecyclerView De A 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();
}

no 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
fonte
O link pode conter uma resposta, mas tente explicar algumas partes substanciais das explicações no link da sua postagem.
Ian
0

Nenhuma dessas respostas leva em consideração se a lista é muito pequena ou não.

Aqui está um pedaço de código que estou usando que funciona no RecycleViews nas duas direções.

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

        });
    }
Oliver Dixon
fonte
0

Deixei minha aproximação. Funciona bem para mim.

Espero que ajude você.

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

}
Dani
fonte
0

Esta solução funciona perfeitamente para mim.

//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);
Giedrius Šlikas
fonte