-
-
Save imran0101/b6d4b3d65b0b4775a22e to your computer and use it in GitHub Desktop.
/** | |
* Custom Scroll listener for RecyclerView. | |
* Based on implementation https://gist.github.com/ssinss/e06f12ef66c51252563e | |
*/ | |
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener { | |
public static String TAG = "EndlessScrollListener"; | |
private int previousTotal = 0; // The total number of items in the dataset after the last load | |
private boolean loading = true; // True if we are still waiting for the last set of data to load. | |
private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more. | |
int firstVisibleItem, visibleItemCount, totalItemCount; | |
private int currentPage = 1; | |
RecyclerViewPositionHelper mRecyclerViewHelper; | |
@Override | |
public void onScrolled(RecyclerView recyclerView, int dx, int dy) { | |
super.onScrolled(recyclerView, dx, dy); | |
mRecyclerViewHelper = RecyclerViewPositionHelper.createHelper(recyclerView); | |
visibleItemCount = recyclerView.getChildCount(); | |
totalItemCount = mRecyclerViewHelper.getItemCount(); | |
firstVisibleItem = mRecyclerViewHelper.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; | |
} | |
} | |
//Start loading | |
public abstract void onLoadMore(int currentPage); | |
} |
/** | |
* RecyclerView position helper class for any LayoutManager. | |
* | |
* compile 'com.android.support:recyclerview-v7:22.0.0' | |
*/ | |
import android.support.v7.widget.OrientationHelper; | |
import android.support.v7.widget.RecyclerView; | |
import android.view.View; | |
public class RecyclerViewPositionHelper { | |
final RecyclerView recyclerView; | |
final RecyclerView.LayoutManager layoutManager; | |
RecyclerViewPositionHelper(RecyclerView recyclerView) { | |
this.recyclerView = recyclerView; | |
this.layoutManager = recyclerView.getLayoutManager(); | |
} | |
public static RecyclerViewPositionHelper createHelper(RecyclerView recyclerView) { | |
if (recyclerView == null) { | |
throw new NullPointerException("Recycler View is null"); | |
} | |
return new RecyclerViewPositionHelper(recyclerView); | |
} | |
/** | |
* Returns the adapter item count. | |
* | |
* @return The total number on items in a layout manager | |
*/ | |
public int getItemCount() { | |
return layoutManager == null ? 0 : layoutManager.getItemCount(); | |
} | |
/** | |
* Returns the adapter position of the first visible view. This position does not include | |
* adapter changes that were dispatched after the last layout pass. | |
* | |
* @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if | |
* there aren't any visible items. | |
*/ | |
public int findFirstVisibleItemPosition() { | |
final View child = findOneVisibleChild(0, layoutManager.getChildCount(), false, true); | |
return child == null ? RecyclerView.NO_POSITION : recyclerView.getChildAdapterPosition(child); | |
} | |
/** | |
* Returns the adapter position of the first fully visible view. This position does not include | |
* adapter changes that were dispatched after the last layout pass. | |
* | |
* @return The adapter position of the first fully visible item or | |
* {@link RecyclerView#NO_POSITION} if there aren't any visible items. | |
*/ | |
public int findFirstCompletelyVisibleItemPosition() { | |
final View child = findOneVisibleChild(0, layoutManager.getChildCount(), true, false); | |
return child == null ? RecyclerView.NO_POSITION : recyclerView.getChildAdapterPosition(child); | |
} | |
/** | |
* Returns the adapter position of the last visible view. This position does not include | |
* adapter changes that were dispatched after the last layout pass. | |
* | |
* @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if | |
* there aren't any visible items | |
*/ | |
public int findLastVisibleItemPosition() { | |
final View child = findOneVisibleChild(layoutManager.getChildCount() - 1, -1, false, true); | |
return child == null ? RecyclerView.NO_POSITION : recyclerView.getChildAdapterPosition(child); | |
} | |
/** | |
* Returns the adapter position of the last fully visible view. This position does not include | |
* adapter changes that were dispatched after the last layout pass. | |
* | |
* @return The adapter position of the last fully visible view or | |
* {@link RecyclerView#NO_POSITION} if there aren't any visible items. | |
*/ | |
public int findLastCompletelyVisibleItemPosition() { | |
final View child = findOneVisibleChild(layoutManager.getChildCount() - 1, -1, true, false); | |
return child == null ? RecyclerView.NO_POSITION : recyclerView.getChildAdapterPosition(child); | |
} | |
View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible, | |
boolean acceptPartiallyVisible) { | |
OrientationHelper helper; | |
if (layoutManager.canScrollVertically()) { | |
helper = OrientationHelper.createVerticalHelper(layoutManager); | |
} else { | |
helper = OrientationHelper.createHorizontalHelper(layoutManager); | |
} | |
final int start = helper.getStartAfterPadding(); | |
final int end = helper.getEndAfterPadding(); | |
final int next = toIndex > fromIndex ? 1 : -1; | |
View partiallyVisible = null; | |
for (int i = fromIndex; i != toIndex; i += next) { | |
final View child = layoutManager.getChildAt(i); | |
final int childStart = helper.getDecoratedStart(child); | |
final int childEnd = helper.getDecoratedEnd(child); | |
if (childStart < end && childEnd > start) { | |
if (completelyVisible) { | |
if (childStart >= start && childEnd <= end) { | |
return child; | |
} else if (acceptPartiallyVisible && partiallyVisible == null) { | |
partiallyVisible = child; | |
} | |
} else { | |
return child; | |
} | |
} | |
} | |
return partiallyVisible; | |
} | |
} |
findLastCompletelyVisibleItemPosition always returns -1( RecyclerView.NO_POSITION ). Can you help me?
In landscape mode, findLastCompletelyVisibleItemPosition is always -1. Does anyone know how to get around this?
I found this when trying to prioritize lazy loading a list. I needed it to be performant so I wasn't very happy with constantly calling findOneVisibleChild().
if anyone has no problem with accuracy, say you just want to get a range to put into priority queue like I did, you can add this into adapter (or wrap your adapter) like this:
override fun onBindViewHolder(presenter: ResultPresenter, i: Int) {
//..
mViewBoundEvents.onNext(i)
}
private val mViewBoundEvents = PublishSubject.create<Int>()
override fun onViewRecycled(holder: ResultPresenter?) {
super.onViewRecycled(holder)
holder?.run{ mViewRecycledEvents.onNext(adapterPosition) }
}
private val mViewRecycledEvents = BehaviorSubject.create(-1)
val windows = Observable
.combineLatest(mViewRecycledEvents, mViewBoundEvents) { o, i ->
val range = o + 1 to i
with(range) { if (first > second) second to first else this }
}
Note that this will give the window a bit wider than what is shown at still and skip items when scroll fast
Thanks you very much
I like the solution form @sonhanguyen. Monitoring per scolled pixel change is expensive. Should check based on the position the adapter is serving.
Thanks you , it is many helpful me. 👍