12

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?

StefMa
  • 3,344
  • 4
  • 27
  • 48
Andrey Rankov
  • 1,964
  • 2
  • 17
  • 33

1 Answers1

56

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.

Eric
  • 5,323
  • 6
  • 30
  • 35
Michael Hays
  • 6,878
  • 2
  • 21
  • 17
  • 2
    Thank you for the clarification. Found one more solution here: http://stackoverflow.com/a/30041459/5571200 – Andrey Rankov Feb 14 '16 at 13:02
  • i made this, but it don't gives me the wanted result https://gist.github.com/beersheba/cdb69cf600d2d1b9f3c5 `float scale = interp(Math.abs(myMidpoint - recyclerMidpoint));` And what is `interp` here? – Andrey Rankov Feb 14 '16 at 14:23
  • `interp` represents an interpolating function that takes a number and returns some scale that will go from 0.0f to 1.0f (and possibly larger). What you are passing is a distance that will most likely be in the 10s or 100s. Not what you want. I'll edit the answer to give you an example interpolation function that you can use. – Michael Hays Feb 15 '16 at 06:56
  • also need to add some smoothScroll. zoom is working, but doing it discrete, not smooth. – Andrey Rankov Feb 15 '16 at 07:11
  • 2
    I was unhappy with the direction I was sending you. I changed the approach to override the layout manager directly. I know I said it was a pain, but I was away on vacation. Once I got home, I coded it up quickly to make sure it looked nice. It was easier than I thought, and since I could actually test it, I felt more confident posting actual code. I would not bother with the scroll hooks. – Michael Hays Feb 15 '16 at 18:25
  • @MichaelHays What if I wanted to have a peek of the next item? How incorporate that? – portfoliobuilder Apr 17 '18 at 21:16
  • how do you keep all child views Height and width same leaving the focused one? – hemen Jul 17 '18 at 06:10
  • Is it possible to start from the center item instead of the start point? Can scrollVerticallyBy(0, recycler, state) "0" value be the center of the recyclcerView item instead of zero? I opened a question here: https://stackoverflow.com/questions/52317646/start-my-recyclerview-horizontal-carousel-from-the-center-item – Red M Sep 13 '18 at 20:26
  • To start with first item centered horizontally you can add padding at start and end: padding = (screenWith - itemWidth)/2 and set android:clipToPadding="false" to RecyclerView. – sgallego Nov 20 '19 at 14:34
  • 3
    USE THIS: https://github.com/yarolegovich/DiscreteScrollView . It's awesome – HF_ Jan 11 '20 at 10:45
  • Thanks @HF_ for suggesting DiscreteScrollView, this did help me alot !!! – Thien Ngan Jun 25 '22 at 14:58