简体   繁体   中英

Implementing auto fling at an interval on a Recycler View in android

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())
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM