I have a recycler view with the following attributes in the xml file.
NOTE : I AM DISPLAYING ONLY ONE ITEM OF AT A TIME ON THE SCREEN FOR THIS RECYCLER VIEW.
<MyCustomRecyclerView
android:id="@+id/my_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:orientation="horizontal"
android:overScrollMode="never"
android:paddingHorizontal="4dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
And I am using a PagerSnapHelper to move to the position left or right based on the center of the view on the screen.
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(this)
It's working fine for a manual scroll action performed.
Now, I want to add an auto scroll as well after a certain interval of time (say 2.5 seconds). I have created a handler and posted a runnable on it with a delay of 2.5 seconds. I am trying to call fling(velocityX, velocityY)
of the RecyclerView with a good enough value of velocityX
val scrollHandler = Handler()
val SCROLL_INTERVAL:Long = 2500 //scroll period in ms
val runnable = object : Runnable {
override fun run() {
//velocityX = 7500
fling(7500, 0)
scrollHandler.postDelayed(this, SCROLL_INTERVAL.toLong())
}
}
But the PagerSnaperHelper::findTargetSnapPosition()
not returning correct target position because the View actually has not changed on the screen as in case of a manual scroll. It is returning the position of the element which is already visible on the screen.
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
int velocityY) {
final int itemCount = layoutManager.getItemCount();
if (itemCount == 0) {
return RecyclerView.NO_POSITION;
}
final OrientationHelper orientationHelper = getOrientationHelper(layoutManager);
if (orientationHelper == null) {
return RecyclerView.NO_POSITION;
}
// A child that is exactly in the center is eligible for both before and after
View closestChildBeforeCenter = null;
int distanceBefore = Integer.MIN_VALUE;
View closestChildAfterCenter = null;
int distanceAfter = Integer.MAX_VALUE;
// Find the first view before the center, and the first view after the center
final int childCount = layoutManager.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = layoutManager.getChildAt(i);
if (child == null) {
continue;
}
final int distance = distanceToCenter(layoutManager, child, orientationHelper);
if (distance <= 0 && distance > distanceBefore) {
// Child is before the center and closer then the previous best
distanceBefore = distance;
closestChildBeforeCenter = child;
}
if (distance >= 0 && distance < distanceAfter) {
// Child is after the center and closer then the previous best
distanceAfter = distance;
closestChildAfterCenter = child;
}
}
// Return the position of the first child from the center, in the direction of the fling
final boolean forwardDirection = isForwardFling(layoutManager, velocityX, velocityY);
if (forwardDirection && closestChildAfterCenter != null) {
return layoutManager.getPosition(closestChildAfterCenter);
} else if (!forwardDirection && closestChildBeforeCenter != null) {
return layoutManager.getPosition(closestChildBeforeCenter);
}
// There is no child in the direction of the fling. Either it doesn't exist (start/end of
// the list), or it is not yet attached (very rare case when children are larger then the
// viewport). Extrapolate from the child that is visible to get the position of the view to
// snap to.
View visibleView = forwardDirection ? closestChildBeforeCenter : closestChildAfterCenter;
if (visibleView == null) {
return RecyclerView.NO_POSITION;
}
int visiblePosition = layoutManager.getPosition(visibleView);
int snapToPosition = visiblePosition
+ (isReverseLayout(layoutManager) == forwardDirection ? -1 : +1);
if (snapToPosition < 0 || snapToPosition >= itemCount) {
return RecyclerView.NO_POSITION;
}
return snapToPosition;
}
I would like to know how can I achieve the desired result?
I got a workaround to solve this. Before calling fling()
, I called scrollBy(x,y)
to scroll the items as if it would have happened during a manual scroll.
val runnable = object : Runnable {
override fun run() {
scrollBy(400,0)
//velocityX = 7500
fling(7500, 0)
scrollHandler.postDelayed(this, SCROLL_INTERVAL.toLong())
}
}