背景
Android 有一个标准的 ProgressBar,当不确定时有一个特殊的动画。还有很多可用的各种进度 View 库(here)。
问题
在我搜索的所有内容中,我找不到做一件非常简单的事情的方法:
有一个从 X 色到 Y 色的渐变,水平显示,并在 X 坐标中移动,这样 X 之前的颜色就会变为 Y 色。
例如(只是一个插图),如果我有一个蓝色渐变<->红色,从边缘到边缘,它会这样:
我尝试过的
我已经尝试过 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
但遗憾的是,它们都是关于 Android 的标准 ProgressBar View ,这意味着它以不同的方式显示可绘制对象的动画。
我也尝试在 Android Arsenal 网站上找到类似的东西,但即使有很多不错的东西,我也找不到这样的东西。
当然,我可以自己制作 2 个 View 的动画,每个 View 都有自己的渐变(一个与另一个相反),但我相信还有更好的方法。
问题
是否可以使用 Drawable 或它的动画,使渐变(或其他任何东西)以这种方式移动(当然是重复)?
也许只是从 ImageView 扩展并在那里为 drawable 设置动画?
是否也可以设置容器的多少将用于重复绘制?我的意思是,在上面的例子中,它可能是从蓝色到红色,所以蓝色会在边缘,红色会在中间。
编辑:
好的,我已经取得了一点进展,但是我不确定运动是否正常,并且我认为它的速度不会像它应该的那样一致,以防 CPU 有点忙,因为它不考虑丢帧。我所做的是一个接一个地绘制 2 个 GradientDrawables,如下所示:
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()
}
}
用法:
horizontalProgressView.setGradientColors(intArrayOf(Color.RED, Color.BLUE))
结果(它确实循环良好,只是很难编辑视频):
所以我现在的问题是,我应该怎么做才能确保它的动画效果很好,即使 UI 线程有点忙?
只是 invalidate
对我来说似乎不是一种可靠的方法,独自一人。我认为它应该检查更多。也许它可以使用一些动画 API 来代替 interpolator 。
最佳答案
我决定将“pskink”答案放在 Kotlin 中(来源 here)。我在这里写它只是因为其他解决方案要么不起作用,要么是解决方法而不是我所询问的。
class ScrollingGradient(private val pixelsPerSecond: Float) : Drawable(), Animatable, TimeAnimator.TimeListener {
private val paint = Paint()
private var x: Float = 0.toFloat()
private val animator = TimeAnimator()
init {
animator.setTimeListener(this)
}
override fun onBoundsChange(bounds: Rect) {
paint.shader = LinearGradient(0f, 0f, bounds.width().toFloat(), 0f, Color.WHITE, Color.BLUE, Shader.TileMode.MIRROR)
}
override fun draw(canvas: Canvas) {
canvas.clipRect(bounds)
canvas.translate(x, 0f)
canvas.drawPaint(paint)
}
override fun setAlpha(alpha: Int) {}
override fun setColorFilter(colorFilter: ColorFilter?) {}
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
override fun start() {
animator.start()
}
override fun stop() {
animator.cancel()
}
override fun isRunning(): Boolean = animator.isRunning
override fun onTimeUpdate(animation: TimeAnimator, totalTime: Long, deltaTime: Long) {
x = pixelsPerSecond * totalTime / 1000
invalidateSelf()
}
}
用法:
MainActivity.kt
val px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200f, resources.getDisplayMetrics())
progress.indeterminateDrawable = ScrollingGradient(px)
activity_main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"
tools:context=".MainActivity">
<ProgressBar
android:id="@+id/progress" style="?android:attr/progressBarStyleHorizontal" android:layout_width="200dp"
android:layout_height="20dp" android:indeterminate="true"/>
</LinearLayout>
关于android - 如何创建一个重复的动画移动渐变drawable,就像一个不确定的进度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48520655/