Skip to content

Instantly share code, notes, and snippets.

@FaithDeveloper
Created June 24, 2023 10:36
Show Gist options
  • Save FaithDeveloper/d98b40681dacc2467e7f0d139691496e to your computer and use it in GitHub Desktop.
Save FaithDeveloper/d98b40681dacc2467e7f0d139691496e to your computer and use it in GitHub Desktop.
Line ViewPager2 Indicator Decoration
package com.test.recyclerpagersnap
/**
* reference : https://github.com/bleeding182/recyclerviewItemDecorations/blob/master/app/src/main/java/com/github/bleeding182/recyclerviewdecorations/viewpager/LinePagerIndicatorDecoration.java
*/
import android.content.res.Resources
import android.graphics.*
import android.util.Log
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.Interpolator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
class LinePagerIndicatorDecoration : ItemDecoration() {
private val colorActive = -0x1
private val colorInactive = Color.parseColor("#22000000")
// private val colorInactive = 0x1
/**
* Height of the space the indicator takes up at the bottom of the view.
*/
private val indicatorHeight = (DP * 32).toInt()
/**
* Indicator stroke width.
*/
private val mIndicatorStrokeWidth = DP * 2
/**
* Indicator width.
*/
private val indicatorItemLength = DP * 100
/**
* Padding between indicators.
*/
private val indicatorItemPadding = DP * 0
/**
* Padding bottom indicators.
*/
private val indicatorItemBottomPadding = DP * 32
/**
* Some more natural animation interpolation
*/
private val mInterpolator: Interpolator = AccelerateDecelerateInterpolator()
private val mPaint = Paint()
init {
mPaint.strokeCap= Paint.Cap.ROUND
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?: return
// center horizontally, calculate width and subtract half from center
val totalLength = indicatorItemLength * itemCount
val paddingBetweenItems = 0.coerceAtLeast(itemCount - 1) * indicatorItemPadding
val indicatorTotalWidth = totalLength + paddingBetweenItems
val indicatorStartX = (parent.width- indicatorTotalWidth) / 2f
// center vertically in the allotted space
val indicatorPosY = parent.height- indicatorItemBottomPadding
drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount)
// find active page (which should be highlighted)
val layoutManager = parent.layoutManageras? LinearLayoutManager ?: return
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) ?: return
val left = activeChild.left
val width = activeChild.width
// on swipe the active item will be positioned from [-width, 0]
// interpolate offset for smooth animation
val progress = mInterpolator.getInterpolation(left * -1 / width.toFloat())
drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress, itemCount)
}
private fun drawLine(startPosX: Float, endPosX:Float, startPosY: Float, endPosY: Float){
val path = Path()
path.moveTo(startPosX, startPosY)
}
private fun drawInactiveIndicators(c: Canvas, indicatorStartX: Float, indicatorPosY: Float, itemCount: Int) {
mPaint.color= colorInactive
val path = Path()
path.moveTo(indicatorStartX, indicatorPosY)
// width of item indicator including padding
val itemWidth = indicatorItemLength + indicatorItemPadding
// val itemWidth = indicatorItemLength
var start = indicatorStartX
for (i in 0untilitemCount) {
// draw the line for every item
path.lineTo(start + indicatorItemLength, indicatorPosY)
start += itemWidth
}
c.drawPath(path, mPaint)
}
private fun drawHighlights(
c: Canvas, indicatorStartX: Float, indicatorPosY: Float,
highlightPosition: Int, progress: Float, itemCount: Int
) {
mPaint.color= colorActive
val path = Path()
// // width of item indicator including padding
val itemWidth = indicatorItemLength + indicatorItemPadding
Log.d("@@@@", "progress $progress")
if (progress == 0f) {
// no swipe, draw a normal indicator
val highlightStart = indicatorStartX + itemWidth * highlightPosition
path.moveTo(highlightStart, indicatorPosY)
path.lineTo(highlightStart + indicatorItemLength, indicatorPosY)
c.drawPath(path, mPaint)
} else {
var highlightStart = indicatorStartX + itemWidth * highlightPosition
// calculate partial highlight
val partialLength = indicatorItemLength * progress
path.moveTo(highlightStart + partialLength, indicatorPosY)
path.lineTo(highlightStart + indicatorItemLength, indicatorPosY)
c.drawPath(path, 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
// )
path.moveTo(highlightStart, indicatorPosY)
path.lineTo(highlightStart + partialLength, indicatorPosY)
c.drawPath(path, mPaint)
}
}
}
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
outRect.bottom = indicatorHeight
}
companion object {
private val DP = Resources.getSystem().displayMetrics.density
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment