Background
Android has a standard ProgressBar with a special animation when being indeterminate . There are also plenty of libraries of so many kinds of progress views that are available (here).
The problem
In all that I've searched, I can't find a way to do a very simple thing:
Have a gradient from color X to color Y, that shows horizontally, and moves in X coordinate so that the colors before X will go to color Y.
For example (just an illustration) , if I have a gradient of blue<->red , from edge to edge , it would go as such:
What I've tried
I've tried some solutions offered here on StackOverflow:
- Change horizontal progress bar indeterminate color
- How to change android indeterminate ProgressBar color?
- Custom Drawable for ProgressBar/ProgressDialog
- How to change progress bar's progress color in Android
- How to Change Horizontal ProgressBar start color and end Color gradient
but sadly they all are about the standard ProgressBar view of Android, which means it has a different way of showing the animation of the drawable.
I've also tried finding something similar on Android Arsenal website, but even though there are many nice ones, I couldn't find such a thing.
Of course, I could just animate 2 views myself, each has a gradient of its own (one opposite of the other), but I'm sure there is a better way.
The question
Is is possible to use a Drawable or an animation of it, that makes a gradient (or anything else) move this way (repeating of course)?
Maybe just extend from ImageView and animate the drawable there?
Is it also possible to set how much of the container will be used for the repeating drawable ? I mean, in the above example, it could be from blue to red, so that the blue will be on the edges, and the red color would be in the middle .
EDIT:
OK, I've made a bit of a progress, but I'm not sure if the movement is ok, and I think that it won't be consistent in speed as it should, in case the CPU is a bit busy, because it doesn't consider frame drops. What I did is to draw 2 GradientDrawables one next to another, as such:
class HorizontalProgressView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val speedInPercentage = 1.5f
private var xMovement: Float = 0.0f
private val rightDrawable: GradientDrawable = GradientDrawable()
private val leftDrawable: GradientDrawable = GradientDrawable()
init {
if (isInEditMode)
setGradientColors(intArrayOf(Color.RED, Color.BLUE))
rightDrawable.gradientType = GradientDrawable.LINEAR_GRADIENT;
rightDrawable.orientation = GradientDrawable.Orientation.LEFT_RIGHT
rightDrawable.shape = GradientDrawable.RECTANGLE;
leftDrawable.gradientType = GradientDrawable.LINEAR_GRADIENT;
leftDrawable.orientation = GradientDrawable.Orientation.RIGHT_LEFT
leftDrawable.shape = GradientDrawable.RECTANGLE;
}
fun setGradientColors(colors: IntArray) {
rightDrawable.colors = colors
leftDrawable.colors = colors
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthSize = View.MeasureSpec.getSize(widthMeasureSpec)
val heightSize = View.MeasureSpec.getSize(heightMeasureSpec)
rightDrawable.setBounds(0, 0, widthSize, heightSize)
leftDrawable.setBounds(0, 0, widthSize, heightSize)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
if (xMovement < width) {
canvas.translate(xMovement, 0.0f)
rightDrawable.draw(canvas)
canvas.translate(-width.toFloat(), 0.0f)
leftDrawable.draw(canvas)
} else {
//now the left one is actually on the right
canvas.translate(xMovement - width, 0.0f)
leftDrawable.draw(canvas)
canvas.translate(-width.toFloat(), 0.0f)
rightDrawable.draw(canvas)
}
canvas.restore()
xMovement += speedInPercentage * width / 100.0f
if (isInEditMode)
return
if (xMovement >= width * 2.0f)
xMovement = 0.0f
invalidate()
}
}
usage:
horizontalProgressView.setGradientColors(intArrayOf(Color.RED, Color.BLUE))
And the result (it does loop well, just hard to edit the video) :
So my question now is, what should I do to make sure it animates well, even if the UI thread is a bit busy ?
It's just that the invalidate
doesn't seem a reliable way to me to do it, alone. I think it should check more than that. Maybe it could use some animation API instead, with interpolator .