Skip to content

Instantly share code, notes, and snippets.

@pedrex1987
Created June 2, 2020 23:30
Show Gist options
  • Save pedrex1987/5f87dca16eafb4d4407e211db6a1dd7f to your computer and use it in GitHub Desktop.
Save pedrex1987/5f87dca16eafb4d4407e211db6a1dd7f to your computer and use it in GitHub Desktop.
dots indicator for recycler view
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