Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView & SnapHelper -> get snapped / targeted view

I very recently started using the SnapHelper provided inside the Support Library v24 in order to have a nice horizontal RecyclerView much like in the Google Play Store.

My question is simple.

By using a RecyclerView.OnScrollListener I can be notified when the scroll state is settling (much like with ViewPager.OnPageChangedListener. This is important because when the scroll is settling, I need to animate the view that will be centered as the default behavior of the LinearSnapHelper. That animation shows that the focus is brought to the soon-to-be centered item.

For now, I managed to get the view that is centered when the scroll state is idle via the following line:

final int centered = (llm.findLastCompletelyVisibleItemPosition() - llm.findFirstCompletelyVisibleItemPosition())  / 2 + llm.findFirstVisibleItemPosition();  // where llm is my LinearLayoutManager

Problem is, starting the animation when the scroll is completed is weird: I really need to smoothly play the animation as the scroll is settling so that when the scroll is idle, the animation completes as well.

So as you can see, I have hard time finding out which item of my RecyclerView will be the one centered.


Here are the solutions I tried so far.

  1. I tried using the same calculation when scroll state is set to RecyclerView.SCROLL_STATE_SETTLING. Unfortunately, calling the findVisiblePosition methods from the adapter when the scroll isn't idle is a bad idea : depending on the scroll speed, the visible items, when these so-called methods are invoked, might not be exactly the same when the scroll is complete. Indeed it will tell me which items are visible at the time of the call. That makes total sense but I needed to try to find that out...

  2. I tried extending the LinearSnapHelper so that when onFling(...) is called I can either use findSnapView(...) to get the view that will be snapped or use findTargetSnapPosition(...) hopefully telling me which view is to be centered when the scroll completes ;

  3. I also tried, when scroll state is RecyclerView.SCROLL_STATE_SETTLING, to use findSnapView(...)

Of all these solutions, none passed multiple tests (trying with different swipe strength & speed). Some got it right on a few occasions but got it wrong in different circumstances therefore rendering the solution unusable.

Having looked inside the LinearSnapHelper I think I started to understand that, somehow, after a swipe, this class computes the total scroll and snaps views on multiple occasions as long as necessary before the scroll completes (amount of distance to scroll) therefore knowing which view will be the final snapped view ... unless I got it all wrong because snapped is not centered?

Anyway, my main question is: How can I know which item of my RecyclerView will be centered after a swipe, regardless of speed and strength of said swipe?

like image 302
Mackovich Avatar asked Oct 30 '16 22:10

Mackovich


People also ask

What is RecyclerView?

RecyclerView is the ViewGroup that contains the views corresponding to your data. It's a view itself, so you add RecyclerView into your layout the way you would add any other UI element. Each individual element in the list is defined by a view holder object.

What is an example of RecyclerView?

The RecyclerView class extends the ViewGroup class and implements ScrollingView interface. It is introduced in Marshmallow. It is an advanced version of the ListView with improved performance and other benefits.

Why is RecyclerView used?

In Android, RecyclerView provides an ability to implement the horizontal, vertical and Expandable List. It is mainly used when we have data collections whose elements can change at run time based on user action or any network events. For using this widget we have to specify the Adapter and Layout Manager.

What is difference between RecyclerView and ListView?

Simple answer: You should use RecyclerView in a situation where you want to show a lot of items, and the number of them is dynamic. ListView should only be used when the number of items is always the same and is limited to the screen size.


1 Answers

Edit:

As a few have pointed out, this is not reliable, particularly when the snap occurs as the result of a slow swipe rather than a fling.

I'd recommend using a RecyclerView.OnScrollListener, and checking the position once the RecyclerView has 'settled'. There's a good tutorial on this here

https://medium.com/over-engineering/detecting-snap-changes-with-androids-recyclerview-snaphelper-9e9f5e95c424


Previous answer:

You need to subclass SnapHelper and override findTargetSnapPosition. The position calculated by super.findTargetSnapPosition(layoutManager, velocityX, velocityY) is the position your RecyclerView is about to snap to.

SnapHelper snapHelper = new LinearSnapHelper() {
    @Override
    public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
        int snapPosition = super.findTargetSnapPosition(layoutManager, velocityX, velocityY);

        // Do something with snapPosition

        return snapPosition;
    }
};
like image 129
Tim Malseed Avatar answered Oct 21 '22 01:10

Tim Malseed