0

I am using cardview inside a RecyclerView and to make the RecyclerView scroll horizontal i have initialized the view with a layout manager like below:

LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);

At the moment the items in the view are scrolling endlessly/smooth. I would like it to stop when one item is shown on screen, almost like a snappy effect. Can this be achieved?

Thanks in advance.

BradleyIW
  • 1,338
  • 2
  • 20
  • 37
user3133966
  • 193
  • 1
  • 12

2 Answers2

3

I used this class:

SnappyRecyclerView

package icn.premierandroid.misc;

import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;

public class SnappyRecyclerView extends RecyclerView {

// Use it with a horizontal LinearLayoutManager
// Based on http://stackoverflow.com/a/29171652/4034572

public SnappyRecyclerView(Context context) {
    super(context);
}

public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}

public SnappyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
public boolean fling(int velocityX, int velocityY) {

    LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();

    int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;

    // views on the screen
    int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
    View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
    int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
    View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);

    // distance we need to scroll
    int leftMargin = (screenWidth - lastView.getWidth()) / 2;
    int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
    int leftEdge = lastView.getLeft();
    int rightEdge = firstView.getRight();
    int scrollDistanceLeft = leftEdge - leftMargin;
    int scrollDistanceRight = rightMargin - rightEdge;

    if (Math.abs(velocityX) < 1000) {
        // The fling is slow -> stay at the current page if we are less than half through,
        // or go to the next page if more than half through

        if (leftEdge > screenWidth / 2) {
            // go to next page
            smoothScrollBy(-scrollDistanceRight, 0);
        } else if (rightEdge < screenWidth / 2) {
            // go to next page
            smoothScrollBy(scrollDistanceLeft, 0);
        } else {
            // stay at current page
            if (velocityX > 0) {
                smoothScrollBy(-scrollDistanceRight, 0);
            } else {
                smoothScrollBy(scrollDistanceLeft, 0);
            }
        }
        return true;

    } else {
        // The fling is fast -> go to next page

        if (velocityX > 0) {
            smoothScrollBy(scrollDistanceLeft, 0);
        } else {
            smoothScrollBy(-scrollDistanceRight, 0);
        }
        return true;

    }

}

@Override
public void onScrollStateChanged(int state) {
    super.onScrollStateChanged(state);

    // If you tap on the phone while the RecyclerView is scrolling it will stop in the middle.
    // This code fixes this. This code is not strictly necessary but it improves the behaviour.

    if (state == SCROLL_STATE_IDLE) {
        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();

        int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;

        // views on the screen
        int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition();
        View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition);
        int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
        View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition);

        // distance we need to scroll
        int leftMargin = (screenWidth - lastView.getWidth()) / 2;
        int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth();
        int leftEdge = lastView.getLeft();
        int rightEdge = firstView.getRight();
        int scrollDistanceLeft = leftEdge - leftMargin;
        int scrollDistanceRight = rightMargin - rightEdge;

        if (leftEdge > screenWidth / 2) {
            smoothScrollBy(-scrollDistanceRight, 0);
        } else if (rightEdge < screenWidth / 2) {
            smoothScrollBy(scrollDistanceLeft, 0);
        }
    }
}

}

in XML (put your package route to the class e.g. mine is icn.premierandroid.misc.SnappyRecyclerView:

 <packagename.SnappyRecyclerView
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/recycler_view"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:scrollbars="none"
            android:layout_weight="0.34" />

You shouldn't need to change anything if you have a RecyclerView initialized in your class already.

Like so:

recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
                // LinearLayoutManager is used here, this will layout the elements in a similar fashion
                // to the way ListView would layout elements. The RecyclerView.LayoutManager defines how
                // elements are laid out.
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(mLayoutManager);

This should satisfy full width of screen elements only, as you've asked for.

BradleyIW
  • 1,338
  • 2
  • 20
  • 37
1

What you are looking for, is the snapping effect.

I've not personally used this class but I believe this would work for you.

https://gist.github.com/lauw/fc84f7d04f8c54e56d56

What this does is, extend the current Android's recyclerview and adds snapping functionality to it.

Add this class to your project and replace the recyclerview with your current recyclerview.

You can enabled snapping of your items to the screen using the setSnapEnabled() method.

ElamParithi Arul
  • 377
  • 1
  • 11