The most direct way to affect what you want is to extend LinearLayoutManager
.
As you've no doubt discovered, hooking into the scroll events properly is a pain:
So let's extend the manager. We'll create a few parameters that you might expose.
public class ZoomCenterCardLayoutManager extends LinearLayoutManager {
// Shrink the cards around the center up to 50%
private final float mShrinkAmount = 0.5f;
// The cards will be at 50% when they are 75% of the way between the
// center and the edge.
private final float mShrinkDistance = 0.75f;
Fill out your constructors, and then override scrollHorizontallyBy
:
@Override
public int scrollHorizontallyBy(int dx,
RecyclerView.Recycler recycler, RecyclerView.State state) {
Call the parent's version and save the distance travelled. We'll need to return this at the end of the method:
int scrolled = super.scrollHorizontallyBy(dx, recycler, state);
We are going to set up a simple linear interpolation. It looks nice enough.
float midpoint = getWidth() / 2.f;
float d0 = 0.f;
float d1 = mShrinkDistance * midpoint;
float s0 = 1.f;
float s1 = 1.f - mShrinkAmount;
Loop through all of the active children of the control, run the interpolation, and set the scale of the child.
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;
}
This is almost all you need. One final step is to make sure that this adjustment is called after initialization -- otherwise the zooming won't take effect until the first time the control is moved:
@Override
public void onLayoutChildren(Recycler recycler, State state) {
super.onLayoutChildren(recycler, state);
scrollHorizontallyBy(0, recycler, state);
}
}
And that's all there is to it. Super responsive, and you can drop this new layout manager into any horizontal recycler.