Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a page indicator for horizontal recyclerview

Any idea how to add page indicator

Any idea how to create a page indicator for recyclerview list ?

like image 549
Nima Avatar asked Nov 21 '15 08:11

Nima


People also ask

What is PagerSnapHelper?

PagerSnapHelper can help achieve a similar behavior to ViewPager . Set both RecyclerView and the items of the RecyclerView. Adapter to have ViewGroup. LayoutParams. MATCH_PARENT height and width and then attach PagerSnapHelper to the RecyclerView using SnapHelper.

What is LayoutManager in RecyclerView?

This wear-specific implementation of LinearLayoutManager provides basic offsetting logic for updating child layout. A LayoutManager is responsible for measuring and positioning item views within a RecyclerView as well as determining the policy for when to recycle item views that are no longer visible to the user.


2 Answers

You can add an indicator by using RecyclerView.ItemDecoration.

Just draw some lines or circles at the bottom and use layoutManager.findFirstVisibleItemPosition() to get the current active item. Since pagers tend to fill the whole width this is an accruate way of getting the displayed item. This also allows us to calculate the scrolling distance by comparing the childs left edge to the parents.

Below you find a sample decoration that draws some lines and animates between them

public class LinePagerIndicatorDecoration extends RecyclerView.ItemDecoration {    private int colorActive = 0xFFFFFFFF;   private int colorInactive = 0x66FFFFFF;    private static final float DP = Resources.getSystem().getDisplayMetrics().density;    /**    * Height of the space the indicator takes up at the bottom of the view.    */   private final int mIndicatorHeight = (int) (DP * 16);    /**    * Indicator stroke width.    */   private final float mIndicatorStrokeWidth = DP * 2;    /**    * Indicator width.    */   private final float mIndicatorItemLength = DP * 16;   /**    * Padding between indicators.    */   private final float mIndicatorItemPadding = DP * 4;    /**    * Some more natural animation interpolation    */   private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();    private final Paint mPaint = new Paint();    public LinePagerIndicatorDecoration() {     mPaint.setStrokeCap(Paint.Cap.ROUND);     mPaint.setStrokeWidth(mIndicatorStrokeWidth);     mPaint.setStyle(Paint.Style.STROKE);     mPaint.setAntiAlias(true);   }    @Override   public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {     super.onDrawOver(c, parent, state);      int itemCount = parent.getAdapter().getItemCount();      // center horizontally, calculate width and subtract half from center     float totalLength = mIndicatorItemLength * itemCount;     float paddingBetweenItems = Math.max(0, itemCount - 1) * mIndicatorItemPadding;     float indicatorTotalWidth = totalLength + paddingBetweenItems;     float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2F;      // center vertically in the allotted space     float indicatorPosY = parent.getHeight() - mIndicatorHeight / 2F;      drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount);       // find active page (which should be highlighted)     LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();     int activePosition = layoutManager.findFirstVisibleItemPosition();     if (activePosition == RecyclerView.NO_POSITION) {       return;     }      // find offset of active page (if the user is scrolling)     final View activeChild = layoutManager.findViewByPosition(activePosition);     int left = activeChild.getLeft();     int width = activeChild.getWidth();      // on swipe the active item will be positioned from [-width, 0]     // interpolate offset for smooth animation     float progress = mInterpolator.getInterpolation(left * -1 / (float) width);      drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress, itemCount);   }    private void drawInactiveIndicators(Canvas c, float indicatorStartX, float indicatorPosY, int itemCount) {     mPaint.setColor(colorInactive);      // width of item indicator including padding     final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;      float start = indicatorStartX;     for (int i = 0; i < itemCount; i++) {       // draw the line for every item       c.drawLine(start, indicatorPosY, start + mIndicatorItemLength, indicatorPosY, mPaint);       start += itemWidth;     }   }    private void drawHighlights(Canvas c, float indicatorStartX, float indicatorPosY,                               int highlightPosition, float progress, int itemCount) {     mPaint.setColor(colorActive);      // width of item indicator including padding     final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;      if (progress == 0F) {       // no swipe, draw a normal indicator       float highlightStart = indicatorStartX + itemWidth * highlightPosition;       c.drawLine(highlightStart, indicatorPosY,           highlightStart + mIndicatorItemLength, indicatorPosY, mPaint);     } else {       float highlightStart = indicatorStartX + itemWidth * highlightPosition;       // calculate partial highlight       float partialLength = mIndicatorItemLength * progress;        // draw the cut off highlight       c.drawLine(highlightStart + partialLength, indicatorPosY,           highlightStart + mIndicatorItemLength, indicatorPosY, mPaint);        // draw the highlight overlapping to the next item as well       if (highlightPosition < itemCount - 1) {         highlightStart += itemWidth;         c.drawLine(highlightStart, indicatorPosY,             highlightStart + partialLength, indicatorPosY, mPaint);       }     }   }    @Override   public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {     super.getItemOffsets(outRect, view, parent, state);     outRect.bottom = mIndicatorHeight;   } } 

Which will give you a result like the following

pager indicator


There is also a blog post that goes more into detail about how the decoration works here and the full source code is available at GitHub

like image 77
David Medenjak Avatar answered Oct 06 '22 00:10

David Medenjak


I have changed the code for circles. Removed the the code to draw line and the same is replaced with draw circle methods. Please find below the complete class:

public class CirclePagerIndicatorDecoration extends RecyclerView.ItemDecoration {     private int colorActive = 0xDE000000;     private int colorInactive = 0x33000000;      private static final float DP = Resources.getSystem().getDisplayMetrics().density;      /**      * Height of the space the indicator takes up at the bottom of the view.      */     private final int mIndicatorHeight = (int) (DP * 16);      /**      * Indicator stroke width.      */     private final float mIndicatorStrokeWidth = DP * 4;      /**      * Indicator width.      */     private final float mIndicatorItemLength = DP * 4;     /**      * Padding between indicators.      */     private final float mIndicatorItemPadding = DP * 8;      /**      * Some more natural animation interpolation      */     private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();      private final Paint mPaint = new Paint();      public CirclePagerIndicatorDecoration() {          mPaint.setStrokeWidth(mIndicatorStrokeWidth);         mPaint.setStyle(Paint.Style.STROKE);         mPaint.setAntiAlias(true);     }      @Override     public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {         super.onDrawOver(c, parent, state);          int itemCount = parent.getAdapter().getItemCount();          // center horizontally, calculate width and subtract half from center         float totalLength = mIndicatorItemLength * itemCount;         float paddingBetweenItems = Math.max(0, itemCount - 1) * mIndicatorItemPadding;         float indicatorTotalWidth = totalLength + paddingBetweenItems;         float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2F;          // center vertically in the allotted space         float indicatorPosY = parent.getHeight() - mIndicatorHeight / 2F;          drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount);          // find active page (which should be highlighted)         LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();         int activePosition = layoutManager.findFirstVisibleItemPosition();         if (activePosition == RecyclerView.NO_POSITION) {             return;         }          // find offset of active page (if the user is scrolling)         final View activeChild = layoutManager.findViewByPosition(activePosition);         int left = activeChild.getLeft();         int width = activeChild.getWidth();         int right = activeChild.getRight();          // on swipe the active item will be positioned from [-width, 0]         // interpolate offset for smooth animation         float progress = mInterpolator.getInterpolation(left * -1 / (float) width);          drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress);     }      private void drawInactiveIndicators(Canvas c, float indicatorStartX, float indicatorPosY, int itemCount) {         mPaint.setColor(colorInactive);          // width of item indicator including padding         final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;          float start = indicatorStartX;         for (int i = 0; i < itemCount; i++) {              c.drawCircle(start, indicatorPosY, mIndicatorItemLength / 2F, mPaint);              start += itemWidth;         }     }      private void drawHighlights(Canvas c, float indicatorStartX, float indicatorPosY,                                 int highlightPosition, float progress) {         mPaint.setColor(colorActive);          // width of item indicator including padding         final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;          if (progress == 0F) {             // no swipe, draw a normal indicator             float highlightStart = indicatorStartX + itemWidth * highlightPosition;              c.drawCircle(highlightStart, indicatorPosY, mIndicatorItemLength / 2F, mPaint);          } else {             float highlightStart = indicatorStartX + itemWidth * highlightPosition;             // calculate partial highlight             float partialLength = mIndicatorItemLength * progress + mIndicatorItemPadding*progress;              c.drawCircle(highlightStart + partialLength, indicatorPosY, mIndicatorItemLength / 2F, mPaint);         }     }      @Override     public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {         super.getItemOffsets(outRect, view, parent, state);         outRect.bottom = mIndicatorHeight;     } } 

Many Thanks, LB Gupta Happy Coding !!!!!

like image 40
lbgupta Avatar answered Oct 06 '22 00:10

lbgupta