Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to scale up Recycler View center item while scrolling in android?

I need to always highlight the center item in the recycler view while scrolling by scaling up.

like image 671
Android Avatar asked Sep 28 '15 13:09

Android


3 Answers

You should follow this code, this helped me to scale up the center item in recycler view.

public class CenterZoomLayoutManager extends LinearLayoutManager {

    private final float mShrinkAmount = 0.15f;
    private final float mShrinkDistance = 0.9f;

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

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

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == VERTICAL) {
            int scrolled = super.scrollVerticallyBy(dy, recycler, state);
            float midpoint = getHeight() / 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 =
                        (getDecoratedBottom(child) + getDecoratedTop(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 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;
        }

    }
}

center horizontal view enter image description here

like image 108
Mayank Garg Avatar answered Nov 08 '22 22:11

Mayank Garg


I slimmed down that solution and added the initial resize during onLayoutComplete. I didn't need vertical scrolling, so I took that part out.

class CenterZoomLinearLayoutManager(
    context: Context,
    private val mShrinkDistance: Float = 0.9f,
    val mShrinkAmount: Float = 0.15f
) : LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) {

    override fun onLayoutCompleted(state: RecyclerView.State?) {
        super.onLayoutCompleted(state)
        scaleChildren()
    }

    override fun scrollHorizontallyBy(dx: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State?): Int {
        return if (orientation == HORIZONTAL) {
            super.scrollHorizontallyBy(dx, recycler, state).also { scaleChildren() }
        } else {
            0
        }
    }

    private fun scaleChildren() {
        val midpoint = width / 2f
        val d1 = mShrinkDistance * midpoint
        for (i in 0 until childCount) {
            val child = getChildAt(i) as View
            val d = Math.min(d1, Math.abs(midpoint - (getDecoratedRight(child) + getDecoratedLeft(child)) / 2f))
            val scale = 1f - mShrinkAmount * d / d1
            child.scaleX = scale
            child.scaleY = scale
        }
    }
}

See https://github.com/pcholt/toy-card-carousel

like image 12
Anarchofascist Avatar answered Nov 08 '22 22:11

Anarchofascist


As an addendum to Mayank Garg's answer and the comments it has saying that it does not work for the first or last items, this happens when you are using this class and also adding extra padding to the list at the beginning and end in order for the first item to appear already centered. In these situations, functions like getDecoratedRight() and getDecoratedLeft() will include the extra padding in the sizes they return. This messes up the calculation of the view's midpoint, and hence it won't work for the first and last items.

A solution to this is to detect if the layout manager is displaying the beginning of the list or not, and use a conditional to use a different calculation which uses one of the decorated anchors as origin but then uses the view's halved width in order to find the midpoint.

In other words, in Mayank's code you have:

childMidpoint =
   (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.f;

You can replace this with something similar to the following:

if (findFirstVisibleItemPosition() == 0 && i == 0) {
   childMidPoint = getDecoratedRight(child) - child.getWidth() / 2.f;
} else {
   childMidPoint = getDecoratedLeft(child) + child.getWidth() / 2.f;
}

In other words, this checks that the first child view is, or isn't, the first item in the adapter, and if so uses either the left or right decorated horizontal position to calculate the midpoint by subtracting or adding the item's halved width.

Another simpler alternative is:

childMidpoint = child.getX() + child.getWidth() / 2.0f

But then again, you need to test if this fits other constraints you may have on your layout/views, since there is probably a reason Mayank used getDecoratedLeft() instead of getX().

like image 1
Grzegorz Adam Hankiewicz Avatar answered Nov 08 '22 21:11

Grzegorz Adam Hankiewicz