content: package com.github.airsaid.accountbook.widget.recycler;
  
  import android.content.Context;
  import android.content.res.Resources;
  import android.content.res.TypedArray;
  import android.graphics.Canvas;
  import android.graphics.Paint;
  import android.graphics.Rect;
  import android.graphics.drawable.Drawable;
  import android.support.annotation.ColorRes;
  import android.support.annotation.DimenRes;
  import android.support.annotation.DrawableRes;
  import android.support.v4.content.ContextCompat;
  import android.support.v7.widget.GridLayoutManager;
  import android.support.v7.widget.LinearLayoutManager;
  import android.support.v7.widget.RecyclerView;
  import android.view.View;
  
  /**
   * Created by yqritc on 2015/01/08.
   */
  public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration {
  
      private static final int DEFAULT_SIZE = 2;
      private static final int[] ATTRS = new int[]{
              android.R.attr.listDivider
      };
  
      protected enum DividerType {
          DRAWABLE, PAINT, COLOR
      }
  
      protected DividerType mDividerType = DividerType.DRAWABLE;
      protected VisibilityProvider mVisibilityProvider;
      protected PaintProvider mPaintProvider;
      protected ColorProvider mColorProvider;
      protected DrawableProvider mDrawableProvider;
      protected SizeProvider mSizeProvider;
      protected boolean mShowLastDivider;
      protected boolean mPositionInsideItem;
      private Paint mPaint;
  
      protected FlexibleDividerDecoration(Builder builder) {
          if (builder.mPaintProvider != null) {
              mDividerType = DividerType.PAINT;
              mPaintProvider = builder.mPaintProvider;
          } else if (builder.mColorProvider != null) {
              mDividerType = DividerType.COLOR;
              mColorProvider = builder.mColorProvider;
              mPaint = new Paint();
              setSizeProvider(builder);
          } else {
              mDividerType = DividerType.DRAWABLE;
              if (builder.mDrawableProvider == null) {
                  TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS);
                  final Drawable divider = a.getDrawable(0);
                  a.recycle();
                  mDrawableProvider = new DrawableProvider() {
                      @Override
                      public Drawable drawableProvider(int position, RecyclerView parent) {
                          return divider;
                      }
                  };
              } else {
                  mDrawableProvider = builder.mDrawableProvider;
              }
              mSizeProvider = builder.mSizeProvider;
          }
  
          mVisibilityProvider = builder.mVisibilityProvider;
          mShowLastDivider = builder.mShowLastDivider;
          mPositionInsideItem = builder.mPositionInsideItem;
      }
  
      private void setSizeProvider(Builder builder) {
          mSizeProvider = builder.mSizeProvider;
          if (mSizeProvider == null) {
              mSizeProvider = new SizeProvider() {
                  @Override
                  public int dividerSize(int position, RecyclerView parent) {
                      return DEFAULT_SIZE;
                  }
              };
          }
      }
  
      @Override
      public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
          RecyclerView.Adapter adapter = parent.getAdapter();
          if (adapter == null) {
              return;
          }
  
          int itemCount = adapter.getItemCount();
          int lastDividerOffset = getLastDividerOffset(parent);
          int validChildCount = parent.getChildCount();
          int lastChildPosition = -1;
          for (int i = 0; i < validChildCount; i++) {
              View child = parent.getChildAt(i);
              int childPosition = parent.getChildAdapterPosition(child);
  
              if (childPosition < lastChildPosition) {
                  // Avoid remaining divider when animation starts
                  continue;
              }
              lastChildPosition = childPosition;
  
              if (!mShowLastDivider && childPosition >= itemCount - lastDividerOffset) {
                  // Don't draw divider for last line if mShowLastDivider = false
                  continue;
              }
  
              if (wasDividerAlreadyDrawn(childPosition, parent)) {
                  // No need to draw divider again as it was drawn already by previous column
                  continue;
              }
  
              int groupIndex = getGroupIndex(childPosition, parent);
              if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
                  continue;
              }
  
              Rect bounds = getDividerBound(groupIndex, parent, child);
              switch (mDividerType) {
                  case DRAWABLE:
                      Drawable drawable = mDrawableProvider.drawableProvider(groupIndex, parent);
                      drawable.setBounds(bounds);
                      drawable.draw(c);
                      break;
                  case PAINT:
                      mPaint = mPaintProvider.dividerPaint(groupIndex, parent);
                      c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
                      break;
                  case COLOR:
                      mPaint.setColor(mColorProvider.dividerColor(groupIndex, parent));
                      mPaint.setStrokeWidth(mSizeProvider.dividerSize(groupIndex, parent));
                      c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
                      break;
              }
          }
      }
  
      @Override
      public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) {
          int position = parent.getChildAdapterPosition(v);
          int itemCount = parent.getAdapter().getItemCount();
          int lastDividerOffset = getLastDividerOffset(parent);
          if (!mShowLastDivider && position >= itemCount - lastDividerOffset) {
              // Don't set item offset for last line if mShowLastDivider = false
              return;
          }
  
          int groupIndex = getGroupIndex(position, parent);
          if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
              return;
          }
  
          setItemOffsets(rect, groupIndex, parent);
      }
  
      /**
       * Check if recyclerview is reverse layout
       *
       * @param parent RecyclerView
       * @return true if recyclerview is reverse layout
       */
      protected boolean isReverseLayout(RecyclerView parent) {
          RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
          if (layoutManager instanceof LinearLayoutManager) {
              return ((LinearLayoutManager) layoutManager).getReverseLayout();
          } else {
              return false;
          }
      }
  
      /**
       * In the case mShowLastDivider = false,
       * Returns offset for how many views we don't have to draw a divider for,
       * for LinearLayoutManager it is as simple as not drawing the last child divider,
       * but for a GridLayoutManager it needs to take the span count for the last items into account
       * until we use the span count configured for the grid.
       *
       * @param parent RecyclerView
       * @return offset for how many views we don't have to draw a divider or 1 if its a
       * LinearLayoutManager
       */
      private int getLastDividerOffset(RecyclerView parent) {
          if (parent.getLayoutManager() instanceof GridLayoutManager) {
              GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
              GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
              int spanCount = layoutManager.getSpanCount();
              int itemCount = parent.getAdapter().getItemCount();
              for (int i = itemCount - 1; i >= 0; i--) {
                  if (spanSizeLookup.getSpanIndex(i, spanCount) == 0) {
                      return itemCount - i;
                  }
              }
          }
  
          return 1;
      }
  
      /**
       * Determines whether divider was already drawn for the row the item is in,
       * effectively only makes sense for a grid
       *
       * @param position current view position to draw divider
       * @param parent   RecyclerView
       * @return true if the divider can be skipped as it is in the same row as the previous one.
       */
      private boolean wasDividerAlreadyDrawn(int position, RecyclerView parent) {
          if (parent.getLayoutManager() instanceof GridLayoutManager) {
              GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
              GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
              int spanCount = layoutManager.getSpanCount();
              return spanSizeLookup.getSpanIndex(position, spanCount) > 0;
          }
  
          return false;
      }
  
      /**
       * Returns a group index for GridLayoutManager.
       * for LinearLayoutManager, always returns position.
       *
       * @param position current view position to draw divider
       * @param parent   RecyclerView
       * @return group index of items
       */
      private int getGroupIndex(int position, RecyclerView parent) {
          if (parent.getLayoutManager() instanceof GridLayoutManager) {
              GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
              GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
              int spanCount = layoutManager.getSpanCount();
              return spanSizeLookup.getSpanGroupIndex(position, spanCount);
          }
  
          return position;
      }
  
      protected abstract Rect getDividerBound(int position, RecyclerView parent, View child);
  
      protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent);
  
      /**
       * Interface for controlling divider visibility
       */
      public interface VisibilityProvider {
  
          /**
           * Returns true if divider should be hidden.
           *
           * @param position Divider position (or group index for GridLayoutManager)
           * @param parent   RecyclerView
           * @return True if the divider at position should be hidden
           */
          boolean shouldHideDivider(int position, RecyclerView parent);
      }
  
      /**
       * Interface for controlling paint instance for divider drawing
       */
      public interface PaintProvider {
  
          /**
           * Returns {@link android.graphics.Paint} for divider
           *
           * @param position Divider position (or group index for GridLayoutManager)
           * @param parent   RecyclerView
           * @return Paint instance
           */
          Paint dividerPaint(int position, RecyclerView parent);
      }
  
      /**
       * Interface for controlling divider color
       */
      public interface ColorProvider {
  
          /**
           * Returns {@link android.graphics.Color} value of divider
           *
           * @param position Divider position (or group index for GridLayoutManager)
           * @param parent   RecyclerView
           * @return Color value
           */
          int dividerColor(int position, RecyclerView parent);
      }
  
      /**
       * Interface for controlling drawable object for divider drawing
       */
      public interface DrawableProvider {
  
          /**
           * Returns drawable instance for divider
           *
           * @param position Divider position (or group index for GridLayoutManager)
           * @param parent   RecyclerView
           * @return Drawable instance
           */
          Drawable drawableProvider(int position, RecyclerView parent);
      }
  
      /**
       * Interface for controlling divider size
       */
      public interface SizeProvider {
  
          /**
           * Returns size value of divider.
           * Height for horizontal divider, width for vertical divider
           *
           * @param position Divider position (or group index for GridLayoutManager)
           * @param parent   RecyclerView
           * @return Size of divider
           */
          int dividerSize(int position, RecyclerView parent);
      }
  
      public static class Builder<T extends Builder> {
  
          private Context mContext;
          protected Resources mResources;
          private PaintProvider mPaintProvider;
          private ColorProvider mColorProvider;
          private DrawableProvider mDrawableProvider;
          private SizeProvider mSizeProvider;
          private VisibilityProvider mVisibilityProvider = new VisibilityProvider() {
              @Override
              public boolean shouldHideDivider(int position, RecyclerView parent) {
                  return false;
              }
          };
          private boolean mShowLastDivider = false;
          private boolean mPositionInsideItem = false;
  
          public Builder(Context context) {
              mContext = context;
              mResources = context.getResources();
          }
  
          public T paint(final Paint paint) {
              return paintProvider(new PaintProvider() {
                  @Override
                  public Paint dividerPaint(int position, RecyclerView parent) {
                      return paint;
                  }
              });
          }
  
          public T paintProvider(PaintProvider provider) {
              mPaintProvider = provider;
              return (T) this;
          }
  
          public T color(final int color) {
              return colorProvider(new ColorProvider() {
                  @Override
                  public int dividerColor(int position, RecyclerView parent) {
                      return mContext.getResources().getColor(color);
                  }
              });
          }
  
          public T colorResId(@ColorRes int colorId) {
              return color(ContextCompat.getColor(mContext, colorId));
          }
  
          public T colorProvider(ColorProvider provider) {
              mColorProvider = provider;
              return (T) this;
          }
  
          public T drawable(@DrawableRes int id) {
              return drawable(ContextCompat.getDrawable(mContext, id));
          }
  
          public T drawable(final Drawable drawable) {
              return drawableProvider(new DrawableProvider() {
                  @Override
                  public Drawable drawableProvider(int position, RecyclerView parent) {
                      return drawable;
                  }
              });
          }
  
          public T drawableProvider(DrawableProvider provider) {
              mDrawableProvider = provider;
              return (T) this;
          }
  
          public T size(final int size) {
              return sizeProvider(new SizeProvider() {
                  @Override
                  public int dividerSize(int position, RecyclerView parent) {
                      return size;
                  }
              });
          }
  
          public T sizeResId(@DimenRes int sizeId) {
              return size(mResources.getDimensionPixelSize(sizeId));
          }
  
          public T sizeProvider(SizeProvider provider) {
              mSizeProvider = provider;
              return (T) this;
          }
  
          public T visibilityProvider(VisibilityProvider provider) {
              mVisibilityProvider = provider;
              return (T) this;
          }
  
          public T showLastDivider() {
              mShowLastDivider = true;
              return (T) this;
          }
  
          public T positionInsideItem(boolean positionInsideItem) {
              mPositionInsideItem = positionInsideItem;
              return (T) this;
          }
  
          protected void checkBuilderParams() {
              if (mPaintProvider != null) {
                  if (mColorProvider != null) {
                      throw new IllegalArgumentException(
                              "Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider.");
                  }
                  if (mSizeProvider != null) {
                      throw new IllegalArgumentException(
                              "Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider.");
                  }
              }
          }
      }
  }