Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"permissions": {
"allow": [
"Read",
"Edit",
"Write",
"Bash(git *)",
"Bash(./gradlew *)",
"Bash(chmod *)",
"Bash(java *)",
"Bash(export *)"
]
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ keystore.properties

.claude/worktrees/
.claude/tmp/
.tmp_issue_comments/

.hermes/
.release-secrets/
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,35 @@ DotsIndicator(
dotsIndicator.attachTo(viewPager)
```

#### Per-dot colors (programmatic)
Give each dot its own selected and/or unselected color. Indices beyond the array length fall back to the global `selectedDotColor` / `dotsColor`.

```Kotlin
// Each dot lights up in a different accent color when selected
dotsIndicator.selectedDotColors = intArrayOf(
Color.parseColor("#FF5252"), // dot 0
Color.parseColor("#FF6D00"), // dot 1
Color.parseColor("#FFD600"), // dot 2
Color.parseColor("#69F0AE"), // dot 3
Color.parseColor("#40C4FF"), // dot 4
)

// Optional: custom unselected colors per dot
dotsIndicator.dotColors = intArrayOf(
Color.parseColor("#FFCDD2"),
Color.parseColor("#FFE0B2"),
Color.parseColor("#FFF9C4"),
Color.parseColor("#CCFF90"),
Color.parseColor("#B3E5FC"),
)
```

Java-friendly setters are also available:
```Java
dotsIndicator.setSelectedDotColors(new int[]{Color.RED, Color.GREEN, Color.BLUE});
dotsIndicator.setDotColors(new int[]{Color.LTGRAY, Color.LTGRAY, Color.LTGRAY});
```


## SpringDotsIndicator
![ezgif com-crop 4](https://user-images.githubusercontent.com/15737675/38329136-2c470ef0-384d-11e8-88a8-c8719dc1d0b7.gif) ![ezgif com-crop 5](https://user-images.githubusercontent.com/15737675/38329293-b87f68a4-384d-11e8-8a04-c560c60dac7c.gif)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tbuonomo.dotsindicatorsample.ui.viewpager

import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
Expand All @@ -24,6 +25,16 @@ class DotsIndicatorSampleViewPagerFragment : Fragment() {
binding.springDotsIndicator.attachTo(this)
binding.wormDotsIndicator.attachTo(this)
}

// Per-dot color demo: each dot lights up in its own accent color when selected.
// Dots without an entry in the array fall back to the global selectedDotColor / dotsColor.
binding.dotsIndicator.selectedDotColors = intArrayOf(
Color.parseColor("#FF5252"), // page 0 — red
Color.parseColor("#FF6D00"), // page 1 — orange
Color.parseColor("#FFD600"), // page 2 — yellow
Color.parseColor("#69F0AE"), // page 3 — green
Color.parseColor("#40C4FF"), // page 4 — blue
)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ class DotsIndicator @JvmOverloads constructor(

companion object {
const val DEFAULT_WIDTH_FACTOR = 2.5f

/**
* Returns the color at [index] in [colors], or [fallback] when [colors] is null or
* [index] is out of range. Pure — no Android context required; safe to call from tests.
*/
@JvmStatic
fun resolveColor(colors: IntArray?, index: Int, fallback: Int): Int =
colors?.getOrElse(index) { fallback } ?: fallback
}

private lateinit var linearLayout: LinearLayout
Expand All @@ -33,8 +41,40 @@ class DotsIndicator @JvmOverloads constructor(
refreshDotsColors()
}

/**
* Per-dot selected colors. Dot at position *i* uses `selectedDotColors[i]`; positions
* beyond the array length fall back to [selectedDotColor]. `null` (default) uses
* [selectedDotColor] for every dot — fully backward-compatible.
*
* Java: `indicator.setSelectedDotColors(new int[]{Color.RED, Color.GREEN, Color.BLUE});`
*/
var selectedDotColors: IntArray? = null
set(value) {
field = value
refreshDotsColors()
}

/**
* Per-dot unselected colors. Dot at position *i* uses `dotColors[i]`; positions beyond
* the array length fall back to [dotsColor]. `null` (default) uses [dotsColor] for every
* dot — fully backward-compatible.
*
* Java: `indicator.setDotColors(new int[]{Color.GRAY, Color.LTGRAY, Color.DKGRAY});`
*/
var dotColors: IntArray? = null
set(value) {
field = value
refreshDotsColors()
}

private val argbEvaluator = ArgbEvaluator()

private fun getSelectedColorForIndex(index: Int) =
resolveColor(selectedDotColors, index, selectedDotColor)

private fun getUnselectedColorForIndex(index: Int) =
resolveColor(dotColors, index, dotsColor)

init {
init(attrs)
}
Expand Down Expand Up @@ -90,9 +130,9 @@ class DotsIndicator @JvmOverloads constructor(
val background = DotsGradientDrawable()
background.cornerRadius = dotsCornerRadius
if (isInEditMode) {
background.setColor(if (0 == index) selectedDotColor else dotsColor)
background.setColor(if (0 == index) getSelectedColorForIndex(index) else getUnselectedColorForIndex(index))
} else {
background.setColor(if (pager!!.currentItem == index) selectedDotColor else dotsColor)
background.setColor(if (pager!!.currentItem == index) getSelectedColorForIndex(index) else getUnselectedColorForIndex(index))
}
imageView.setBackgroundCompat(background)

Expand Down Expand Up @@ -138,20 +178,23 @@ class DotsIndicator @JvmOverloads constructor(
val selectedDotBackground = selectedDot.background as DotsGradientDrawable
val nextDotBackground = nextDot.background as DotsGradientDrawable

if (selectedDotColor != dotsColor) {
val selSelectedColor = getSelectedColorForIndex(selectedPosition)
val selUnselectedColor = getUnselectedColorForIndex(selectedPosition)
val nxtSelectedColor = getSelectedColorForIndex(nextPosition)
val nxtUnselectedColor = getUnselectedColorForIndex(nextPosition)

if (selSelectedColor != selUnselectedColor || nxtSelectedColor != nxtUnselectedColor) {
val selectedColor = argbEvaluator.evaluate(
positionOffset, selectedDotColor,
dotsColor
positionOffset, selSelectedColor, selUnselectedColor
) as Int
val nextColor = argbEvaluator.evaluate(
positionOffset, dotsColor,
selectedDotColor
positionOffset, nxtUnselectedColor, nxtSelectedColor
) as Int

nextDotBackground.setColor(nextColor)

if (progressMode && selectedPosition <= pager!!.currentItem) {
selectedDotBackground.setColor(selectedDotColor)
selectedDotBackground.setColor(selSelectedColor)
} else {
selectedDotBackground.setColor(selectedColor)
}
Expand All @@ -165,7 +208,7 @@ class DotsIndicator @JvmOverloads constructor(
dots[position].setWidth(dotsSize.toInt())
val elevationItem = dots[position]
val background = elevationItem.background as? DotsGradientDrawable ?: return
background.setColor(dotsColor)
background.setColor(getUnselectedColorForIndex(position))
elevationItem.setBackgroundCompat(background)
elevationItem.invalidate()
}
Expand All @@ -181,9 +224,9 @@ class DotsIndicator @JvmOverloads constructor(

background?.let {
if (index == pager!!.currentItem || progressMode && index < pager!!.currentItem) {
background.setColor(selectedDotColor)
background.setColor(getSelectedColorForIndex(index))
} else {
background.setColor(dotsColor)
background.setColor(getUnselectedColorForIndex(index))
}
}

Expand Down
Loading
Loading