Eu tenho este código para um RecyclerView.
recyclerView = (RecyclerView)rootview.findViewById(R.id.fabric_recyclerView);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new RV_Item_Spacing(5));
FabricAdapter fabricAdapter=new FabricAdapter(ViewAdsCollection.getFabricAdsDetailsAsArray());
recyclerView.setAdapter(fabricAdapter);
Preciso saber quando o RecyclerView atinge a posição mais inferior durante a rolagem. É possível ? Se sim, como?
android
scroll
android-recyclerview
Adrian
fonte
fonte
Respostas:
também há uma maneira simples de fazer isso
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (!recyclerView.canScrollVertically(1)) { Toast.makeText(YourActivity.this, "Last", Toast.LENGTH_LONG).show(); } } });
inteiros de direção: -1 para cima, 1 para baixo, 0 sempre retornará falso.
fonte
if (!recyclerView.canScrollVertically(1) && !mHasReachedBottomOnce) { mHasReachedBottomOnce = true Toast.makeText(YourActivity.this, "Last", Toast.LENGTH_LONG).show();}
newState == RecyclerView.SCROLL_STATE_IDLE
àif
declaração funcionaria.Use este código para evitar chamadas repetidas
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (!recyclerView.canScrollVertically(1) && newState==RecyclerView.SCROLL_STATE_IDLE) { Log.d("-----","end"); } } });
fonte
Basta implementar um addOnScrollListener () em sua reciclagem. Em seguida, dentro do ouvinte de rolagem, implemente o código abaixo.
RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (mIsLoading) return; int visibleItemCount = mLayoutManager.getChildCount(); int totalItemCount = mLayoutManager.getItemCount(); int pastVisibleItems = mLayoutManager.findFirstVisibleItemPosition(); if (pastVisibleItems + visibleItemCount >= totalItemCount) { //End of list } } };
fonte
'findFirstVisibleItemPosition'
emandroid.support.v7.widget.RecyclerView
(recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
. Este trabalho.A resposta está em Kotlin, funcionará em Java. O IntelliJ deve convertê-lo para você, se você copiar e colar.
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener(){ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { // 3 lines below are not needed. Log.d("TAG","Last visible item is: ${gridLayoutManager.findLastVisibleItemPosition()}") Log.d("TAG","Item count is: ${gridLayoutManager.itemCount}") Log.d("TAG","end? : ${gridLayoutManager.findLastVisibleItemPosition() == gridLayoutManager.itemCount-1}") if(gridLayoutManager.findLastVisibleItemPosition() == gridLayoutManager.itemCount-1){ // We have reached the end of the recycler view. } super.onScrolled(recyclerView, dx, dy) } })
Isso também funcionará
LinearLayoutManager
porque tem os mesmos métodos usados acima. Ou seja,findLastVisibleItemPosition()
egetItemCount()
(itemCount
em Kotlin).fonte
Eu não estava obtendo uma solução perfeita com as respostas acima porque estava disparando duas vezes, mesmo em
onScrolled
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) if( !recyclerView.canScrollVertically(RecyclerView.FOCUS_DOWN)) context?.toast("Scroll end reached") }
fonte
recyclerView.canScrollVertically(direction)
direção é qualquer número inteiro + vs - então pode 4 se estiver na parte inferior ou -12 se estiver no topo.Tente isto
Eu usei as respostas acima que ele sempre roda quando você chega ao final da visualização do reciclador,
Se você quiser verificar apenas uma vez se está em um fundo ou não? Exemplo: - Se eu tiver a lista de 10 itens sempre que for no final ela me exibirá e novamente se eu rolar de cima para baixo ela não imprimirá novamente, e se você adicionar mais listas e for lá, ela será exibida novamente.
Nota: - Use este método ao lidar com compensação ao acertar API
Crie uma classe chamada EndlessRecyclerViewScrollListener
import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener { // The minimum amount of items to have below your current scroll position // before loading more. private int visibleThreshold = 5; // The current offset index of data you have loaded private int currentPage = 0; // The total number of items in the dataset after the last load private int previousTotalItemCount = 0; // True if we are still waiting for the last set of data to load. private boolean loading = true; // Sets the starting page index private int startingPageIndex = 0; RecyclerView.LayoutManager mLayoutManager; public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager) { this.mLayoutManager = layoutManager; } // public EndlessRecyclerViewScrollListener() { // this.mLayoutManager = layoutManager; // visibleThreshold = visibleThreshold * layoutManager.getSpanCount(); // } public EndlessRecyclerViewScrollListener(StaggeredGridLayoutManager layoutManager) { this.mLayoutManager = layoutManager; visibleThreshold = visibleThreshold * layoutManager.getSpanCount(); } public int getLastVisibleItem(int[] lastVisibleItemPositions) { int maxSize = 0; for (int i = 0; i < lastVisibleItemPositions.length; i++) { if (i == 0) { maxSize = lastVisibleItemPositions[i]; } else if (lastVisibleItemPositions[i] > maxSize) { maxSize = lastVisibleItemPositions[i]; } } return maxSize; } // This happens many times a second during a scroll, so be wary of the code you place here. // We are given a few useful parameters to help us work out if we need to load some more data, // but first we check if we are waiting for the previous load to finish. @Override public void onScrolled(RecyclerView view, int dx, int dy) { int lastVisibleItemPosition = 0; int totalItemCount = mLayoutManager.getItemCount(); if (mLayoutManager instanceof StaggeredGridLayoutManager) { int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null); // get maximum element within the list lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions); } else if (mLayoutManager instanceof GridLayoutManager) { lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition(); } else if (mLayoutManager instanceof LinearLayoutManager) { lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition(); } // 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; } // 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. // threshold should reflect how many total columns there are too if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) { currentPage++; onLoadMore(currentPage, totalItemCount, view); loading = true; } } // Call this method whenever performing new searches public void resetState() { this.currentPage = this.startingPageIndex; this.previousTotalItemCount = 0; this.loading = true; } // Defines the process for actually loading more data based on page public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view); }
use esta classe assim
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener( linearLayoutManager) { @Override public void onLoadMore(int page, int totalItemsCount, RecyclerView view) { Toast.makeText(getActivity(),"LAst",Toast.LENGTH_LONG).show(); } });
Está funcionando perfeitamente na minha extremidade, avise-me se você estiver tendo algum problema
fonte
Aí está a minha implementação, é muito útil para
StaggeredGridLayout
.Uso:
private EndlessScrollListener scrollListener = new EndlessScrollListener(new EndlessScrollListener.RefreshList() { @Override public void onRefresh(int pageNumber) { //end of the list } }); rvMain.addOnScrollListener(scrollListener);
Implementação do ouvinte:
class EndlessScrollListener extends RecyclerView.OnScrollListener { private boolean isLoading; private boolean hasMorePages; private int pageNumber = 0; private RefreshList refreshList; private boolean isRefreshing; private int pastVisibleItems; EndlessScrollListener(RefreshList refreshList) { this.isLoading = false; this.hasMorePages = true; this.refreshList = refreshList; } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); StaggeredGridLayoutManager manager = (StaggeredGridLayoutManager) recyclerView.getLayoutManager(); int visibleItemCount = manager.getChildCount(); int totalItemCount = manager.getItemCount(); int[] firstVisibleItems = manager.findFirstVisibleItemPositions(null); if (firstVisibleItems != null && firstVisibleItems.length > 0) { pastVisibleItems = firstVisibleItems[0]; } if (visibleItemCount + pastVisibleItems >= totalItemCount && !isLoading) { isLoading = true; if (hasMorePages && !isRefreshing) { isRefreshing = true; new Handler().postDelayed(new Runnable() { @Override public void run() { refreshList.onRefresh(pageNumber); } }, 200); } } else { isLoading = false; } } public void noMorePages() { this.hasMorePages = false; } void notifyMorePages() { isRefreshing = false; pageNumber = pageNumber + 1; } interface RefreshList { void onRefresh(int pageNumber); } }
fonte
Depois de não estar satisfeito com a maioria das outras respostas neste tópico, encontrei algo que acho melhor e não está em nenhum lugar aqui.
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (!recyclerView.canScrollVertically(1) && dy > 0) { //scrolled to bottom }else if (!recyclerView.canScrollVertically(-1) && dy < 0) { //scrolled to bottom } } });
Isso é simples e atingirá exatamente uma vez em todas as condições quando você tiver rolado para cima ou para baixo.
fonte
Eu também estava procurando por essa pergunta, mas não encontrei a resposta que me satisfizesse, então criei minha própria realização do recyclerView.
outras soluções são menos precisas que a minha. por exemplo: se o último item for muito grande (muito texto), o retorno de chamada de outras soluções virá muito mais cedo, então o recyclerView realmente atingiu o fundo.
minha solução consertar esse problema.
class CustomRecyclerView: RecyclerView{ abstract class TopAndBottomListener{ open fun onBottomNow(onBottomNow:Boolean){} open fun onTopNow(onTopNow:Boolean){} } constructor(c:Context):this(c, null) constructor(c:Context, attr:AttributeSet?):super(c, attr, 0) constructor(c:Context, attr:AttributeSet?, defStyle:Int):super(c, attr, defStyle) private var linearLayoutManager:LinearLayoutManager? = null private var topAndBottomListener:TopAndBottomListener? = null private var onBottomNow = false private var onTopNow = false private var onBottomTopScrollListener:RecyclerView.OnScrollListener? = null fun setTopAndBottomListener(l:TopAndBottomListener?){ if (l != null){ checkLayoutManager() onBottomTopScrollListener = createBottomAndTopScrollListener() addOnScrollListener(onBottomTopScrollListener) topAndBottomListener = l } else { removeOnScrollListener(onBottomTopScrollListener) topAndBottomListener = null } } private fun createBottomAndTopScrollListener() = object :RecyclerView.OnScrollListener(){ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { checkOnTop() checkOnBottom() } } private fun checkOnTop(){ val firstVisible = linearLayoutManager!!.findFirstCompletelyVisibleItemPosition() if(firstVisible == 0 || firstVisible == -1 && !canScrollToTop()){ if (!onTopNow) { onTopNow = true topAndBottomListener?.onTopNow(true) } } else if (onTopNow){ onTopNow = false topAndBottomListener?.onTopNow(false) } } private fun checkOnBottom(){ var lastVisible = linearLayoutManager!!.findLastCompletelyVisibleItemPosition() val size = linearLayoutManager!!.itemCount - 1 if(lastVisible == size || lastVisible == -1 && !canScrollToBottom()){ if (!onBottomNow){ onBottomNow = true topAndBottomListener?.onBottomNow(true) } } else if(onBottomNow){ onBottomNow = false topAndBottomListener?.onBottomNow(false) } } private fun checkLayoutManager(){ if (layoutManager is LinearLayoutManager) linearLayoutManager = layoutManager as LinearLayoutManager else throw Exception("for using this listener, please set LinearLayoutManager") } private fun canScrollToTop():Boolean = canScrollVertically(-1) private fun canScrollToBottom():Boolean = canScrollVertically(1) }
então em sua atividade / fragmento:
override fun onCreate() { customRecyclerView.layoutManager = LinearLayoutManager(context) } override fun onResume() { super.onResume() customRecyclerView.setTopAndBottomListener(this) } override fun onStop() { super.onStop() customRecyclerView.setTopAndBottomListener(null) }
espero que ajude alguém ;-)
fonte
Esta é a minha solução:
val onScrollListener = object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { directionDown = dy > 0 } override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { if (recyclerView.canScrollVertically(1).not() && state != State.UPDATING && newState == RecyclerView.SCROLL_STATE_IDLE && directionDown) { state = State.UPDATING // TODO do what you want when you reach bottom, direction // is down and flag for non-duplicate execution } } }
fonte
state
esse enum?Podemos usar a interface para obter a posição
Interface: crie uma interface para o ouvinte
public interface OnTopReachListener { void onTopReached(int position);}
Atividade:
mediaRecycleAdapter = new MediaRecycleAdapter(Class.this, taskList); recycle.setAdapter(mediaRecycleAdapter); mediaRecycleAdapter.setOnSchrollPostionListener(new OnTopReachListener() { @Override public void onTopReached(int position) { Log.i("Position","onTopReached "+position); } });
Adaptador:
public void setOnSchrollPostionListener(OnTopReachListener topReachListener) { this.topReachListener = topReachListener;}@Override public void onBindViewHolder(MyViewHolder holder, int position) {if(position == 0) { topReachListener.onTopReached(position);}}
fonte