J'ai ce code pour un 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);
J'ai besoin de savoir quand le RecyclerView atteint la position la plus basse en défilant. C'est possible ? Si oui comment?
il y a aussi un moyen simple de le faire
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();
}
}
});
entiers de direction: -1 pour up, 1 pour down, 0 retournera toujours faux.
Il suffit de mettre en œuvre un addOnScrollListener () sur votre liste de recyclage. Ensuite, dans l'écouteur de défilement, implémentez le code ci-dessous.
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
}
}
};
La réponse est en Kotlin, cela fonctionnera en Java. IntelliJ devrait le convertir pour vous si vous copiez et collez.
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)
}
})
Cela fonctionnera également pour LinearLayoutManager
car il utilise les mêmes méthodes que celles utilisées ci-dessus. À savoir findLastVisibleItemPosition()
et getItemCount()
(itemCount
en Kotlin).
Utilisez ce code pour éviter les appels répétés
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");
}
}
});
Voilà mon implémentation, elle est très utile pour StaggeredGridLayout
.
tilisation:
private EndlessScrollListener scrollListener =
new EndlessScrollListener(new EndlessScrollListener.RefreshList() {
@Override public void onRefresh(int pageNumber) {
//end of the list
}
});
rvMain.addOnScrollListener(scrollListener);
Implémentation de l'auditeur:
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);
} }
J'ai utilisé les réponses ci-dessus, il est toujours affiché quand vous allez à la fin de la vue du recycleur,
Si vous ne voulez vérifier qu'une seule fois si c'est sur un fond ou non? Exemple: - Si j'ai la liste de 10 éléments chaque fois que je vais au bas, il m'indiquera et encore si je fais défiler De haut en bas, il ne sera pas imprimé à nouveau. Si vous ajoutez d'autres listes et que vous y accédez, il s'affichera à nouveau.
Remarque: - Utilisez cette méthode lorsque vous traitez avec offset en tapant dans l'API
Créez une classe nommée 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);
}
utiliser cette classe comme ça
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();
}
});
Son fonctionnement parfait à ma fin, me contacter si vous rencontrez un problème
Je cherchais aussi cette question, mais je n’ai pas trouvé la réponse qui me satisfaisait. Je crée donc ma propre réalisation de recyclerView.
les autres solutions sont moins précises que les miennes. Par exemple, si le dernier élément est assez volumineux (beaucoup de texte), le rappel d’autres solutions interviendra beaucoup plus tôt que recyclerView vraiment atteint le fond.
ma solution résoud ce problème.
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)
}
puis dans votre activité/fragment:
override fun onCreate() {
customRecyclerView.layoutManager = LinearLayoutManager(context)
}
override fun onResume() {
super.onResume()
customRecyclerView.setTopAndBottomListener(this)
}
override fun onStop() {
super.onStop()
customRecyclerView.setTopAndBottomListener(null)
}
j'espère que ça va aider quelqu'un ;-)
Ceci est ma solution:
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
}
}
}
Les réponses ci-dessus ne m'apportaient pas une solution parfaite, car il se déclenchait deux fois, même sur 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")
}
Ma solution pour LinearLayoutManager horizontal (Kotlin):
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx:Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dx == 0 && !recyclerView.canScrollHorizontally(1)) {
//add here your code
recyclerView.removeOnScrollListener(this)//remove this listener to prevent leaks
}
}
})
</ code>
recyclerView.layoutManager? .scrollToPosition (recyclerView.adapter? .itemCount? .minus (1) !!) // défile jusqu'à la dernière position de l'élément pour déclencher OnScrollListener
Nous pouvons utiliser l'interface pour obtenir la position
Interface: Créer une interface pour le listener
public interface OnTopReachListener { void onTopReached(int position);}
Activité:
mediaRecycleAdapter = new MediaRecycleAdapter(Class.this, taskList); recycle.setAdapter(mediaRecycleAdapter); mediaRecycleAdapter.setOnSchrollPostionListener(new OnTopReachListener() {
@Override
public void onTopReached(int position) {
Log.i("Position","onTopReached "+position);
}
});
Adaptateur:
public void setOnSchrollPostionListener(OnTopReachListener topReachListener) {
this.topReachListener = topReachListener;}@Override public void onBindViewHolder(MyViewHolder holder, int position) {if(position == 0) {
topReachListener.onTopReached(position);}}