-
-
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; | |
| } | |
| } |
so nice, Thank you
works perfect but how can i get the viewholder with the return position??
+1
+1
+1
Thanks you , it is many helpful me. 👍
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.
@hwangjr RecyclerView is passed as part of the method parameter.
The current state and update recyclerView will be available in the onScrolled method.