Created
June 2, 2020 23:30
-
-
Save pedrex1987/5f87dca16eafb4d4407e211db6a1dd7f to your computer and use it in GitHub Desktop.
dots indicator for recycler view
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package br.com.petz.seres.commons.custom | |
import android.graphics.Canvas | |
import android.graphics.Paint | |
import android.graphics.Rect | |
import android.view.View | |
import android.view.animation.AccelerateDecelerateInterpolator | |
import android.view.animation.Interpolator | |
import androidx.annotation.ColorInt | |
import androidx.recyclerview.widget.LinearLayoutManager | |
import androidx.recyclerview.widget.RecyclerView | |
class CirclePagerIndicatorDecoration : RecyclerView.ItemDecoration() { | |
companion object { | |
private val DP: Float = android.content.res.Resources.getSystem().displayMetrics.density | |
} | |
private var colorActive = -0x22000000 | |
private var colorInactive = 0x33000000 | |
/** | |
* Height of the space the indicator takes up at the bottom of the view. | |
*/ | |
private val mIndicatorHeight = | |
(DP * 16).toInt() | |
/** | |
* Indicator stroke width. | |
*/ | |
private val mIndicatorStrokeWidth = | |
DP * 4 | |
/** | |
* Indicator width. | |
*/ | |
private val mIndicatorItemLength = | |
DP * 4 | |
/** | |
* Padding between indicators. | |
*/ | |
private val mIndicatorItemPadding = | |
DP * 8 | |
/** | |
* Some more natural animation interpolation | |
*/ | |
private val mInterpolator: Interpolator = AccelerateDecelerateInterpolator() | |
private val mPaint: Paint = Paint() | |
init { | |
mPaint.strokeWidth = mIndicatorStrokeWidth | |
mPaint.style = Paint.Style.STROKE | |
mPaint.isAntiAlias = true | |
} | |
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { | |
super.onDrawOver(c, parent, state) | |
val itemCount = parent.adapter!!.itemCount | |
// center horizontally, calculate width and subtract half from center | |
val totalLength = mIndicatorItemLength * itemCount | |
val paddingBetweenItems = | |
Math.max(0, itemCount - 1) * mIndicatorItemPadding | |
val indicatorTotalWidth = totalLength + paddingBetweenItems | |
val indicatorStartX = (parent.width - indicatorTotalWidth) / 2f | |
// center vertically in the allotted space | |
val indicatorPosY = parent.height - mIndicatorHeight / 2f | |
drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount) | |
// find active page (which should be highlighted) | |
val layoutManager = | |
parent.layoutManager as LinearLayoutManager? | |
val activePosition = layoutManager!!.findFirstVisibleItemPosition() | |
if (activePosition == RecyclerView.NO_POSITION) { | |
return | |
} | |
// find offset of active page (if the user is scrolling) | |
val activeChild = layoutManager.findViewByPosition(activePosition) | |
val left = activeChild!!.left | |
val width = activeChild.width | |
val right = activeChild.right | |
// on swipe the active item will be positioned from [-width, 0] | |
// interpolate offset for smooth animation | |
val progress: Float = | |
mInterpolator.getInterpolation(left * -1 / width.toFloat()) | |
drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress) | |
} | |
override fun getItemOffsets( | |
outRect: Rect, | |
view: View, | |
parent: RecyclerView, | |
state: RecyclerView.State | |
) { | |
super.getItemOffsets(outRect, view, parent, state) | |
outRect.top = mIndicatorHeight | |
} | |
private fun drawInactiveIndicators( | |
c: Canvas, | |
indicatorStartX: Float, | |
indicatorPosY: Float, | |
itemCount: Int | |
) { | |
mPaint.color = colorInactive | |
// width of item indicator including padding | |
val itemWidth = mIndicatorItemLength + mIndicatorItemPadding | |
var start = indicatorStartX | |
for (i in 0 until itemCount) { | |
c.drawCircle(start, indicatorPosY, mIndicatorItemLength / 2f, mPaint) | |
start += itemWidth | |
} | |
} | |
private fun drawHighlights( | |
c: Canvas, indicatorStartX: Float, indicatorPosY: Float, | |
highlightPosition: Int, progress: Float | |
) { | |
mPaint.color = colorActive | |
// width of item indicator including padding | |
val itemWidth = mIndicatorItemLength + mIndicatorItemPadding | |
if (progress == 0f) { | |
// no swipe, draw a normal indicator | |
val highlightStart = indicatorStartX + itemWidth * highlightPosition | |
c.drawCircle(highlightStart, indicatorPosY, mIndicatorItemLength / 2f, mPaint) | |
} else { | |
val highlightStart = indicatorStartX + itemWidth * highlightPosition | |
// calculate partial highlight | |
val partialLength = | |
mIndicatorItemLength * progress + mIndicatorItemPadding * progress | |
c.drawCircle( | |
highlightStart + partialLength, | |
indicatorPosY, | |
mIndicatorItemLength / 2f, | |
mPaint | |
) | |
} | |
} | |
fun setDotColors(@ColorInt setColorActive: Int, @ColorInt setColorInactive: Int) { | |
colorActive = setColorActive | |
colorInactive = setColorInactive | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment