8

I'm creating a carousel Horizontal RecyclerView with Zoom on focused item that is starting from the first item of the RecyclerView.

Code for custom CenterZoomLayoutManager:

public class CenterZoomLayoutManager extends LinearLayoutManager {
private final float mShrinkAmount = 0.15f;
private final float mShrinkDistance = 0.9f;

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

@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
    int orientation = getOrientation();
    if (orientation == HORIZONTAL) {

        int scrolled = super.scrollHorizontallyBy(dx, recycler, state);
        float midpoint = getWidth() / 2.f;
        float d0 = 0.f;
        float d1 = mShrinkDistance * midpoint;
        float s0 = 1.f;
        float s1 = 1.f - mShrinkAmount;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            float childMidpoint =
                    (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.f;
            float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
            float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
            child.setScaleX(scale);
            child.setScaleY(scale);
        }
        return scrolled;
    } else return 0;
}

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    super.onLayoutChildren(recycler, state);
    scrollHorizontallyBy(0, recycler, state);
}
}

And in my Fragment, I have the following:

private void onSetRecyclerView() {

    recyclerView = fragmentView.findViewById(R.id.recyclerView);

    if (recyclerView != null) {
        RecyclerView.LayoutManager layoutManager = new CenterZoomLayoutManager(context, LinearLayoutManager.HORIZONTAL,
                false);

        recyclerView.scrollToPosition(storeList.size() / 2);

        final SnapHelper snapHelper = new LinearSnapHelper();
        snapHelper.attachToRecyclerView(recyclerView);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);
    }
}

I want to start my RecyclerView from the center item and not from the start position.

Culprit:
in my CenterZoomLayoutManager, I'm setting the pixel offset start to 0 pixels through scrollHorizontallyBy(0, recycler, state).

Problem:
I cannot find a way to pass the offset pixels created by recyclerView.scrollToPosition(storeList.size() / 2) to the CenterZoomLayoutManager. I tried to search for different built-in method to get the X-offset, but so far no luck.

Red M
  • 2,163
  • 3
  • 20
  • 40
  • can you help me understand the math behind this ? – r4jiv007 Jun 21 '20 at 17:00
  • I posted this almost 2 years ago. I don't freshly remember the mathematical logic, but I can tell you that I did not write this. I copied it from somewhere else. You can copy part of it, and search the net. You will be able to find the original post. As far as the result, I achieved having my layout centered in the middle, and not being close to the horizontal left or right edges of screen after @Cheticamp answer. Hope that helps. – Red M Jun 21 '20 at 23:32

1 Answers1

10

The solution is to change the timing of when LinearSnapHelper is attached to the RecyclerView. The following is a reworked onSetRecyclerView() that will snap the central item of the RecyclerView to the center of the screen. Notice that the LinearSnapHelper is not attached until the RecyclerView is laid out and scrolled appropriately. You do not need to do any scrolling in onLayoutChildren().

private void onSetRecyclerView() {
    recyclerView = findViewById(R.id.recyclerView);
    CenterZoomLayoutManager layoutManager =
        new CenterZoomLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(adapter);
    // Scroll to the position we want to snap to
    layoutManager.scrollToPosition(storeList.size() / 2);
    // Wait until the RecyclerView is laid out.
    recyclerView.post(new Runnable() {
        @Override
        public void run() {
            // Shift the view to snap  near the center of the screen.
            // This does not have to be precise.
            int dx = (recyclerView.getWidth() - recyclerView.getChildAt(0).getWidth()) / 2;
            recyclerView.scrollBy(-dx, 0);
            // Assign the LinearSnapHelper that will initially snap the near-center view.
            LinearSnapHelper snapHelper = new LinearSnapHelper();
            snapHelper.attachToRecyclerView(recyclerView);
        }
    });
}

This is how the initial screen of my test app is displayed. There are 201 "stores."

enter image description here

Cheticamp
  • 50,205
  • 8
  • 64
  • 109
  • 1
    Why are you using index zero for getChildAt() though? – Red M Sep 19 '18 at 14:02
  • @RedM `layoutManager.scrollToPosition(storeList.size() / 2)` will do just enough to get the central view to the screen. In the `RecyclerView`, this will be the first child or the child at index 0. – Cheticamp Sep 19 '18 at 16:20