title: How to mimic view pager with recycler view?
body: <p>One advantage of ViewPager over RecyclerView is that it has a setPageTransformer() listener. Here you can manipulate the page and transform it to do neat animations between or while swiping. I would like mimic being able to transform a page while using RecyclerView.</p>
  
  <p>First, I mimic the behavior of ViewPager using LinearSnapHelper</p>
  
  <pre><code>    LinearSnapHelper snapHelper = new LinearSnapHelper() {
          @Override
          public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
              View centerView = findSnapView(layoutManager);
              if (centerView == null) {
                  return RecyclerView.NO_POSITION;
              }
  
              int position = layoutManager.getPosition(centerView);
              int targetPosition = -1;
              if (layoutManager.canScrollHorizontally()) {
                  if (velocityX &lt; 0) {
                      targetPosition = position - 1;
                  } else {
                      targetPosition = position + 1;
                  }
              }
  
              if (layoutManager.canScrollVertically()) {
                  if (velocityY &lt; 0) {
                      targetPosition = position - 1;
                  } else {
                      targetPosition = position + 1;
                  }
              }
  
              final int firstItem = 0;
              final int lastItem = layoutManager.getItemCount() - 1;
              targetPosition = Math.min(lastItem, Math.max(targetPosition, firstItem));
              return targetPosition;
          }
      };
      snapHelper.attachToRecyclerView(recyclerview);
  </code></pre>
  
  <p>This is working perfectly, and looks really good. Now I would like to transform the pages (items) as I swipe them. With a ViewPager I am able to do this with the following code:</p>
  
  <pre><code>    viewpager.setPageTransformer(false, new ViewPager.PageTransformer() {
          private static final float MIN_SCALE = 0.85f;
  
          public void transformPage(@NonNull View view, float position) {
              int pageWidth = view.getWidth();
              int pageHeight = view.getHeight();
  
              // modify the default slide transition to shrink the page
              float scaleFactor = Math.max(MIN_SCALE, 1 - (Math.abs(position) / 5));
              float vertMargin = pageHeight * (1 - scaleFactor) / 2;
              float horzMargin = pageWidth * (1 - scaleFactor) / 2;
              if (position &lt; 0) {
                  view.setTranslationX(horzMargin - vertMargin / 2);
              } else {
                  view.setTranslationX(-horzMargin + vertMargin / 2);
              }
  
              // scale the page down (between MIN_SCALE and 1)
              view.setScaleX(scaleFactor);
              view.setScaleY(scaleFactor);
          }
      });
  </code></pre>
  
  <p>Since there is no page transformer listener for RecyclerViews, I figured that the equivalent would be on the ScrollChangeListener. So I added the code to that listener</p>
  
  <pre><code>    recyclerview.setOnScrollChangeListener(new View.OnScrollChangeListener() {
          @Override
          public void onScrollChange(View view, int i, int i1, int i2, int i3) {
              int pageWidth = view.getWidth();
              int pageHeight = view.getHeight();
              // modify the default slide transition to shrink the page
              float scaleFactor = Math.max(0.85f, 1 - (Math.abs(mLayoutManager.findFirstVisibleItemPosition()) / 5));
              float vertMargin = pageHeight * (1 - scaleFactor) / 2;
              float horzMargin = pageWidth * (1 - scaleFactor) / 2;
              if (mLayoutManager.findFirstVisibleItemPosition() &lt; 0) {
                  view.setTranslationX(horzMargin - vertMargin / 2);
              } else {
                  view.setTranslationX(-horzMargin + vertMargin / 2);
              }
  
              // scale the page down (between MIN_SCALE and 1)
              view.setScaleX(scaleFactor);
              view.setScaleY(scaleFactor);
          }
      });
  </code></pre>
  
  <p>The page is no scaling at all. Everything seems to be ignored. Where should I put the transformation code at? I want each item to shrink and grow as you switch between items. Think of a Carousal animation e.g. <a href="https://github.com/jgabrielfreitas/android-carousel" rel="noreferrer">https://github.com/jgabrielfreitas/android-carousel</a></p>
  
  <p>EDIT:
  Trying to manage transforming the view in onScroll was too difficult, so I ended up modifying LayoutManager. This works well. I only need to figure out how to do the peek. Here is what I came up with, this has the carousel animation look that I was looking for.</p>
  
  <pre><code>public class CustomLinearLayoutManager extends LinearLayoutManager {
  
      private final float mShrinkAmount = 0.15f;
      private final float mShrinkDistance = 1.0f;
  
      public CustomLinearLayoutManager(Context context) {
          super(context);
      }
  
      @Override
      public int scrollVerticallyBy(int dy, @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) {
          int orientation = getOrientation();
          if (orientation == VERTICAL) {
              int scrolled = super.scrollVerticallyBy(dy, recycler, state);
              float midpoint = getHeight() / 2.0f;
              float d0 = 0.0f;
              float d1 = mShrinkDistance * midpoint;
              float s0 = 1.0f;
              float s1 = 1.0f - mShrinkAmount;
              // loop through active children and set scale of child
              for (int i = 0; i &lt; getChildCount(); i++) {
                  View child = getChildAt(i);
                  float childMidpoint = (getDecoratedBottom(child) + getDecoratedTop(child)) / 2.0f;
                  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, @NonNull RecyclerView.Recycler recycler, @NonNull RecyclerView.State state) {
          int orientation = getOrientation();
          if (orientation == HORIZONTAL) {
              int scrolled = super.scrollHorizontallyBy(dx, recycler, state);
              float midpoint = getWidth() / 2.0f;
              float d0 = 0.0f;
              float d1 = mShrinkDistance * midpoint;
              float s0 = 1.0f;
              float s1 = 1.0f - mShrinkAmount;
              // loop through active children and set scale of child
              for (int i = 0; i &lt; getChildCount(); i++) {
                  View child = getChildAt(i);
                  float childMidpoint = (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.0f;
                  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);
          scrollVerticallyBy(0, recycler, state);
      }
  }
  </code></pre>