【问题标题】:How to animate gradient?如何动画渐变?
【发布时间】:2015-08-26 10:57:49
【问题描述】:

如何设置从 color#1 到 color#2 的渐变动画?类似于

我打算将它用作单位的健康栏(因此,它将是一个以绿色开始并以红色结束的有限动画)

【问题讨论】:

    标签: android animation gradient android-custom-view ondraw


    【解决方案1】:

    在谷歌上搜索时,我发现了 2 种适用于 android 的方法:use ShaderFactory 或扩展 View,使用 new Shader(new LinearGradient())。两个答案都是一样的——每次调用View.onDraw(Canvas canvas) 方法调用new Shader()。如果此类动画渐变的数量超过〜3,它真的很昂贵。

    所以我用了另一种方式。我避免调用new 每个onDraw(),使用单个预先计算的LinearGradient。这就是它的样子(gif,所以动画衰减了):

    诀窍是创建LinearGradient,它比View.getWidth()colorsCount 倍。之后,您可以在绘制渐变时使用canvas.translate() 来更改其颜色,因此根本不会在onDraw() 中调用new

    要创建渐变,您需要当前的宽度和高度。我在onSizeChanged() 中做到了。我也在这里设置了Shader

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    
        width = getWidth();
        height = getHeight();
    
        LinearGradient gradient = new LinearGradient(
                0, height / 2, width * colors.length - 1, height / 2,
                colors, null, Shader.TileMode.REPEAT);
        fillPaint.setShader(gradient);
    
        shapePath = getParallelogrammPath(width, height, sidesGap);
        shapeBorderPath = getParallelogrammPath(width, height, sidesGap);
    }
    

    我使用路径是因为平行四边形视图,你可以使用任何你想要的东西。在实现绘图时,你应该注意两件事:你需要translate()整个canvas在当前偏移和offset()你的填充形状:

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        canvas.translate(-gradientOffset, 0);
        shapePath.offset(gradientOffset, 0f, tempPath);
        canvas.drawPath(tempPath, fillPaint);
        canvas.restore();
    
        canvas.drawPath(shapeBorderPath, borderPaint);
    
        super.onDraw(canvas); // my View is FrameLayout, so need to call it after
    }
    

    你也应该使用canvas.save() & canvas.restore()。它将画布的内部矩阵保存到堆栈并相应地恢复它。

    所以最后你需要做的是动画gradientOffset。你可以使用任何你想要的东西,比如ObjectAnimator (Property Animation)。我使用TimeAnimator,因为我需要控制updateTick并直接开始偏移。这是我的认识(有点困难和苛刻):

    static public final int LIFETIME_DEAFULT = 2300;
    private long lifetime = LIFETIME_DEAFULT, updateTickMs = 25, timeElapsed = 0;
    private long accumulatorMs = 0;
    private float gradientOffset = 0f;
    
    public void startGradientAnimation() {
        stopGradientAnimation();
        resolveTimeElapsed();
    
        final float gradientOffsetCoef = (float) (updateTickMs) / lifetime;
        final int colorsCount = this.colors.length - 1;
        gradientAnimation.setTimeListener(new TimeAnimator.TimeListener() {
            @Override
            public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
                final long gradientWidth = width * colorsCount;
                if (totalTime > (lifetime - timeElapsed)) {
                    animation.cancel();
                    gradientOffset = gradientWidth;
                    invalidate();
                } else {
                    accumulatorMs += deltaTime;
    
                    final long gradientOffsetsCount = accumulatorMs / updateTickMs;
                    gradientOffset += (gradientOffsetsCount * gradientWidth) * gradientOffsetCoef;
                    accumulatorMs %= updateTickMs;
    
                    boolean gradientOffsetChanged = (gradientOffsetsCount > 0) ? true : false;
                    if (gradientOffsetChanged) {
                        invalidate();
                    }
                }
            }
        });
    
        gradientAnimation.start();
    }
    

    您可以找到完整的View 代码here

    【讨论】:

    • 我不明白……你问的问题在同一天同时回答?这从来都不是问题。
    • @Chris-Jr,我搜索了很多,但没有找到我想要的答案。所以我做了一些研究,最后得到了它。然后我想“嗯,也许其他人将来会像我一样搜索它并坚持下去?” - 所以我用答案创造了这个问题。此外,还有一个内置功能可以做到这一点。尝试点击。有支票Answer your own question – share your knowledge, Q&A-style
    • 啊,好吧,我只是被时间弄糊涂了。谢谢你,也谢谢你分享你的解决方案。
    • 感谢 Nexen,这绝对是正确的做法。我很感激,我很高兴得到你的回答,开始我自己的研究
    • 感谢指路,节省了时间。 TimeAnimator 也需要 api16,带有 AnimatorUpdateListener 的 Objectanimator 也可以解决问题。
    【解决方案2】:

    我想出了不同的、更短的方法。只需使用GradientDrawablesetColors (int[] colors) 方法并随着时间的推移对其进行动画处理。

    这是一个示例:

    val start = Color.DKGRAY
    val mid = Color.MAGENTA
    val end = Color.BLUE
    
    //content.background is set as a GradientDrawable in layout xml file 
    val gradient = content.background as GradientDrawable
    
    val evaluator = ArgbEvaluator()
    val animator = TimeAnimator.ofFloat(0.0f, 1.0f)
    animator.duration = 1500
    animator.repeatCount = ValueAnimator.INFINITE
    animator.repeatMode = ValueAnimator.REVERSE
    animator.addUpdateListener {
        val fraction = it.animatedFraction
        val newStart = evaluator.evaluate(fraction, start, end) as Int
        val newMid = evaluator.evaluate(fraction, mid, start) as Int
        val newEnd = evaluator.evaluate(fraction, end, mid) as Int
    
        gradient.colors = intArrayOf(newStart, newMid, newEnd)
    }
    
    animator.start()
    

    结果:

    【讨论】:

      【解决方案3】:

      刚刚重写了@Krzysztof Misztal 对java 的回答:

      public static void startAnimation(final int view, final Activity activity) {
              final int start = Color.parseColor("#FDB72B");
              final int mid = Color.parseColor("#88FDB72B");
              final int end = Color.TRANSPARENT;
      
      
              final ArgbEvaluator evaluator = new ArgbEvaluator();
              View preloader = activity.findViewById(R.id.gradientPreloaderView);
              preloader.setVisibility(View.VISIBLE);
              final GradientDrawable gradient = (GradientDrawable) preloader.getBackground();
      
              ValueAnimator animator = TimeAnimator.ofFloat(0.0f, 1.0f);
              animator.setDuration(500);
              animator.setRepeatCount(ValueAnimator.INFINITE);
              animator.setRepeatMode(ValueAnimator.REVERSE);
              animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                  @Override
                  public void onAnimationUpdate(ValueAnimator valueAnimator) {
                      Float fraction = valueAnimator.getAnimatedFraction();
                      int newStrat = (int) evaluator.evaluate(fraction, start, end);
                      int newMid = (int) evaluator.evaluate(fraction, mid, start);
                      int newEnd = (int) evaluator.evaluate(fraction, end, mid);
                      int[] newArray = {newStrat, newMid, newEnd};
                      gradient.setColors(newArray);
                  }
              });
      
              animator.start();
          }
      
      
      
      
      public static void stopAnimation(final int view, final Activity activity){
      
          ObjectAnimator.ofFloat(activity.findViewById(view), "alpha", 0f).setDuration(125).start();
      }
      

      view 是一个简单的View,带有渐变背景:

      //gradient_preloader
      <?xml version="1.0" encoding="utf-8"?>
      <shape xmlns:android="http://schemas.android.com/apk/res/android"
          android:shape="rectangle">
          <gradient
              android:startColor="#FDB72B"
              android:endColor="#00000000"
              android:angle="0"/>
      </shape>
      

      观点:

      <View
          android:id="@+id/gradientPreloaderView"
          android:layout_width="match_parent"
          android:visibility="gone"
          android:layout_height="@dimen/basic_8_dp"
          android:background="@drawable/gradient_preloader" />
      

      希望我的回答对你有帮助

      【讨论】:

        【解决方案4】:

        最新的替代答案。

        class GradientAnimationDrawable(
          start: Int = Color.rgb(0, 143, 209),
          center: Int = Color.rgb(1, 106, 154),
          end: Int = Color.rgb(28, 179, 249),
          frameDuration: Int = 3000,
          enterFadeDuration: Int = 0,
          exitFadeDuration: Int = 3000
        ) : AnimationDrawable() {
        
          private val gradientStart = GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, intArrayOf(start, center, end))
            .apply {
              shape = GradientDrawable.RECTANGLE
              gradientType = GradientDrawable.LINEAR_GRADIENT
            }
        
          private val gradientCenter = GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, intArrayOf(center, end, start))
            .apply {
              shape = GradientDrawable.RECTANGLE
              gradientType = GradientDrawable.LINEAR_GRADIENT
            }
        
          private val gradientEnd = GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, intArrayOf(end, start, center))
            .apply {
              shape = GradientDrawable.RECTANGLE
              gradientType = GradientDrawable.LINEAR_GRADIENT
            }
        
          init {
            addFrame(gradientStart, frameDuration)
            addFrame(gradientCenter, frameDuration)
            addFrame(gradientEnd, frameDuration)
            setEnterFadeDuration(enterFadeDuration)
            setExitFadeDuration(exitFadeDuration)
            isOneShot = false
          }
        
        }
        
        

        【讨论】:

        • 你能用Java写吗?
        猜你喜欢
        • 1970-01-01
        • 2020-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-01
        相关资源
        最近更新 更多