0

There is such issue, I have horizontal RecyclerView where each cell less than width of screen.

So I found a solution here

RecyclerVIew auto scroll to display all the elements as in News Feed etc.,

All work excellent if one cell take whole width of the screen otherwise(if each cell take 95% of screen width) every auto swipe place the cell at the beginner of screen (right side) and it is logical. So at the end of one visible cell it is start of another cell

enter image description here

it is doesn't looks good. I need this cell to be at the middle of the screen like this.

I need to see previous cell - current - next one

enter image description here

Now I would like to explain some magic how I make current smooth scroll (as I mentioned at link above)

this method in my CustomLinnearLayoutManager

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position)
{
    LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext())
    {
        @Override
        public PointF computeScrollVectorForPosition(int targetPosition)
        {
            return SmoothLayoutManager.this.computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics)
        {
            return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
        }
    };

    linearSmoothScroller.setTargetPosition(position);
    startSmoothScroll(linearSmoothScroller);
}

But this method works without offset

I found out one more method that can provide desired offset

scrollToPositionWithOffset(final int position, final int offset)

And it is looks like exactly what I need , but this method works without smooth animation.

So, eventually my question is : how to apply animation logic from first method to second (that with offset)

Feel free to ask

Sagar Zala
  • 3,925
  • 9
  • 27
  • 54
Aleksey Timoshchenko
  • 3,364
  • 1
  • 33
  • 61

2 Answers2

6

To auto snapping and showing one item at center of RecyclerView, simply you need to use LinearSnapHelper like following:

LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

If you want to scroll to a specific item programmatically, LinearSnapHelper handles snapping functionality too.

SmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
        @Override
        protected int getVerticalSnapPreference() {
            return LinearSmoothScroller.SNAP_TO_ANY;
        }

        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return 120f / displayMetrics.densityDpi;
        }
    };

...

smoothScroller.setTargetPosition(position);
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);

Here is the visual result:
.

..................Manually Scrolling...........................Programmatically Scrolling..........

Example Example

aminography
  • 18,195
  • 11
  • 51
  • 57
  • Yes, it is exactly what I was looking for. But one more question, as you can see when you make auto scroll to exact position first you get this position and than it looks like pull effect to set this cell to center... Maybe you know any way how to do it in one flow? without this pull effect... – Aleksey Timoshchenko Oct 31 '18 at 08:27
  • I have posted my implementation. At the end of it I have a question (take a look please) . If you know how to solve , let me know. Thanks – Aleksey Timoshchenko Oct 31 '18 at 09:06
  • Do you mean you want to go to target position immediately? Without traverse animation? – aminography Oct 31 '18 at 09:27
  • To avoid traversing large number of items when the list is large, you can do a trick like this: https://hastebin.com/imeqidabav.cpp Also it is possible to have multiple scrolling speed (as you do) for traversing various distances. I mean the distance can be divided to multiple sections depend on its length then each section has its own speed. (The closer sections to target position, the lower speed) – aminography Oct 31 '18 at 09:51
  • Not exactly. If you try to make smooth scroll (not fast) by finger - all is ok and cell actually came to middle like it expect, but if you try to make the same scroll but fast, so in this case when it looks like it should be stopped(let's say after 20 cells) actually it doesn't ... it goes farther ... lets say + 20 cells.. and it looks like someone continues scroll more... did you see something like this with your implementation? Try to swipe your finger fast – Aleksey Timoshchenko Oct 31 '18 at 10:00
  • If you repeat exact swiping without setting a SnapHelper, the number of passed items would be the same as setting SnapHelper. The constant scrolling speed caused by SnapHelper makes the sense of extra scrolling. I think we should take a look at android.support.v7.widget.SnapHelper source code. – aminography Oct 31 '18 at 10:33
0

Eventually, I found the way thanks a lot to @aminography for his answer and also one more answer help me a lot

https://stackoverflow.com/a/39654328

Actually now I have such implementation

My custom LinnearLayoutManager implementation

public class SmoothLayoutManager extends LinearLayoutManager
{
public static final int X_25 = 25;
public static final int X_200 = 200;
public static final float DEFAULT = X_25;

/**
 * !! IMPORTANT !!
 * If you need to add new value, don't forget add it here also
 */
@Retention(RetentionPolicy.SOURCE)
@IntDef({X_25, X_200})
private @interface Speed
{

}

private static float MILLISECONDS_PER_INCH = DEFAULT;

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

public SmoothLayoutManager(Context context, int orientation, boolean reverseLayout)
{
    super(context, orientation, reverseLayout);
}

public SmoothLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
    super(context, attrs, defStyleAttr, defStyleRes);
}

public SmoothLayoutManager setSpeedOfSmooth(@Speed int iSpeed)
{
    MILLISECONDS_PER_INCH = iSpeed;

    return this;
}

@Override
public void scrollToPositionWithOffset(final int position, final int offset)
{
    super.scrollToPositionWithOffset(position, offset);
}

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position)
{
    RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext())
    {
        @Override
        public PointF computeScrollVectorForPosition(int targetPosition)
        {
            return SmoothLayoutManager.this.computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected int getVerticalSnapPreference()
        {
            return LinearSmoothScroller.SNAP_TO_ANY;
        }

        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics)
        {
            return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
        }

        @Override
        public int calculateDtToFit(final int viewStart, final int viewEnd, final int boxStart, final int boxEnd, final int snapPreference)
        {
            return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
        }
    };

    smoothScroller.setTargetPosition(position);
    startSmoothScroll(smoothScroller);
}
}

And this is how I make set

private void setRv(Context iC)
    {
        RecyclerView.Adapter adapter = new UpSaleInnerAdapter(mPicasso, mInflater, iLink -> mListener.onButtonClick(iLink));

        mRv.setLayoutManager(new SmoothLayoutManager(iC, LinearLayoutManager.HORIZONTAL, false).setSpeedOfSmooth(SmoothLayoutManager.X_200));
        mRv.setAdapter(adapter);

        SnapHelper snapHelper = new LinearSnapHelper();
        snapHelper.attachToRecyclerView(mRv);
    }

Note :

I noticed that sometimes if you make fast swipe, so SnapHelper a little bit confused and pass more cells that need... like a turbo mode :)

If someone will find how to fix it, let me know.

Thanks!

Aleksey Timoshchenko
  • 3,364
  • 1
  • 33
  • 61