Search code examples
androidzoomingandroid-recyclerviewcarousel

Zoom central image recycler view


I have RecyclerView with images. It based on this solution. Images are lazy loaded into view with Glide. I need to add zoom on central image like in this: example

How can i do it?


Solution

  • 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.