android - 如何创建一个重复的动画移动渐变drawable,就像一个不确定的进度?

标签 android animation drawable android-progressbar

背景

Android 有一个标准的 ProgressBar,当不确定时有一个特殊的动画。还有很多可用的各种进度 View 库(here)。

问题

在我搜索的所有内容中,我找不到做一件非常简单的事情的方法:

有一个从 X 色到 Y 色的渐变,水平显示,并在 X 坐标中移动,这样 X 之前的颜色就会变为 Y 色。

例如(只是一个插图),如果我有一个蓝色渐变<->红色,从边缘到边缘,它会这样:

enter image description here

我尝试过的

我已经尝试过 StackOverflow 上提供的一些解决方案:

但遗憾的是,它们都是关于 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))

结果(它确实循环良好,只是很难编辑视频):

enter image description here

所以我现在的问题是,我应该怎么做才能确保它的动画效果很好,即使 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/

相关文章:

android - 将 xml (android 源代码) 转换为 pdf

android ffmpeg halninja av_open_input_file 返回 -2(没有这样的文件或目录)

android - CSS 硬件加速效果不佳

jQuery:根据窗口滚动位置固定到页面顶部后动画容器宽度

android - 在选择器中更改可绘制对象的颜色

android - 将过渡添加到 AnimationDrawable

android - 如何在添加到 Android AOSP 的自定义项目中设置文件权限

android - 更改 TextView 时的动画

iphone - 如何从左翻转?

javascript - Cordova如何使用drawables