Hey im trying to change a backgroud color between 5 colors in a endless cicle.
what i tried was this
private val myColors = arrayOf(red, yellow, purple, orange, green)
private val colorCycle = TransitionDrawable(myColors)
private fun myFunction() {
this.background = colorCycle
colorCycle.startTransition(2000)
}
Now I'm aware that this implementation only works between 2 colors "An extension of LayerDrawables that is intended to cross-fade between the first and second layer. To start the transition, call startTransition(int). To display just the first layer, call resetTransition()." but i can't find info in how to this that i want to do, an endless cycle between the 5 colors
if someone could point me to the right direction that would be perfect, Thanks!
CodePudding user response:
I don't think there's anything that handles this in the typical libraries - like you say, TransitionDrawable
only handles two layers, and it's more about fading between two things (usually images). You could technically make one for each transition (colour1 to colour2, colour2 to colour3 and so on) and swap them around as you hit the end of each transition, but that's a bit of work.
It's probably easier to make your own utility class that can interpolate between a bunch of colours, something like this:
class ColourCycler(val transitionLength: Int, @ColorInt val colours: List<Int>) {
private var startTime: Long? = null
val currentColour
get(): @ColorInt Int {
// work out how much time has elapsed, and where that falls on the cycle
val elapsed = System.currentTimeMillis() - (startTime ?: 0)
val cyclePosition = ((elapsed / transitionLength) % colours.size).toInt()
val transitionPercent = (elapsed % transitionLength).toFloat() / transitionLength
// grab this colour on the cycle and the next (wrapping around the list)
// and blend them using the ColorUtils package from the androidx.core library
val colour1 = colours[cyclePosition]
val colour2 = colours[(cyclePosition 1) % colours.size]
return ColorUtils.blendARGB(colour1, colour2, transitionPercent)
}
fun start() {
startTime = System.currentTimeMillis()
}
}
That way, once you start()
it, you can poll it whenever you like to get the blended colour for the current time. Here's an approach with coroutines, but you can use any repeating task approach you like - it just needs to get the colour, and update the background:
// in your fragment's onViewCreated or whatever
// initialise your cycle and start it
val cycler = ColourCycler(
transitionLength = 2000, colours = listOf(
Color.parseColor("red"),
Color.parseColor("yellow"),
Color.parseColor("lime"),
Color.parseColor("cyan")
)
).apply { start() }
// set up a repeating task to poll it and update the background
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// run continuously while the coroutine is active (not cancelled)
while (isActive) {
binding.layout.setBackgroundColor(cycler.currentColour)
// update every 16 ms i.e. ~60fps
delay(16)
}
}
}
A better approach would be to hold the ColourCycler
object in a ViewModel
so it survives rotations without getting reinitialised (and starting over at the first colour).
You also might want to drop the whole started state, and just turn the currentColour
property into a pure function that takes an elapsed value and returns the appropriate colour, instead of deciding itself how much time has elapsed. That way you can keep track of the running time elsewhere, and do things like pausing the cycle by storing the current elapsed value, run it backwards, whatever!