【问题标题】:Animate drawing of circle/ arc on canvas在画布上绘制圆/弧的动画
【发布时间】:2013-11-17 18:39:59
【问题描述】:

更新:2013 年 11 月 20 日:这仍未解决。

我正在尝试在自定义视图中动画创建一个圆圈。我想对圆周的创建进行动画处理 - 动画开始时有一个圆弧,到动画结束时,圆是完整的。

我按照这个答案成功地做到了这一点 - https://stackoverflow.com/a/11168363/2442638 - 只需添加一个重复的Handler 以增加sweepAngle 并调用invalidate();

但是,这并不像我希望的那样工作,因为我无法设置完成圆圈的持续时间。

这是我当前的代码:

  Path mOuterCirclePath = new Path();
        final RectF mOval = new RectF();
        int mSweepAngle = 0;
        int mStartAngle = -90;

    @Override
        protected void onDraw(Canvas canvas) {

                mOval.set(0, 0, mBorderRect.right, mBorderRect.bottom); //mBorderRect is the outside border of my view
                mOuterCirclePath.arcTo(mOval, 0, 0, true);
                canvas.drawArc(mOval, -mStartAngle, mSweepAngle, false,
                        mOuterCirclePaint);
    }

        public void drawOuterCircle() {

                startUpdateOuterCircle();

        }

        Runnable mCircleInvalidator = new Runnable() {
            @Override
            public void run() {
                              if (mSweepAngle <= 360) {
                                mSweepAngle++
    }
                invalidate();
                mHandler.postDelayed(mCircleInvalidator, 20); 
            }
        };

        void startUpdateOuterCircle() {
            mCircleInvalidator.run();
        }

        void stopUpdateOuterCircle() {
            mHandler.removeCallbacks(mCircleInvalidator);
        }

主要问题是:如何设置动画的持续时间?我希望它可以轻松更改,就像在动画师课程中一样。

附:据我所知,我不能为此使用任何动画师,例如“ObjectAnimator”的ViewPropertyAnimator。如果我错了,请纠正我!

【问题讨论】:

标签: android android-animation android-canvas android-view android-handler


【解决方案1】:

要使用自定义对象动画器属性您需要创建 getter 和 setter 方法,如 Stackoverflow 答案中所述:

Android Property Animation

就我而言,我有一个 CircleView 类和 sweepAngle 作为变量,如下所示:

public class CircleView extends View
{
    public float sweepAngle = 0.0f; // start at 0 degrees

    ...

    // ---------------------------------------------------
    // getter and setter method to turn "sweepAngle"
    // into a property for ObjectAnimator to use
    // ---------------------------------------------------
    public float getSweepAngle()
    {
        return sweepAngle;
    }

    public void setSweepAngle(float angle)
    {
        sweepAngle = angle;
    }

    // ---------------------------------------------------
    // animation method to be called outside this class
    // ---------------------------------------------------
    public void animateArc(float fromAngle, float toAngle, long duration)
    {
        sweepAngle = fromAngle;

        invalidate();

        ObjectAnimator anim = ObjectAnimator.ofFloat(this, "sweepAngle", fromAngle, toAngle);
        anim.setDuration(duration);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator)
            {
                // calling invalidate(); will trigger onDraw() to execute
                invalidate();
            }
        });
        anim.start();
    }
}

注意:

以上具体示例是 Hithredin 建议使用 ObjectAnimator 的演示。

自定义穷人的实现

我建议你不要使用下面的这段代码,但如果你想知道我是如何完成我自己的自定义实现的,我发现它偏离了 8 毫秒并且只允许线性插值(不容易/ease out),它就像上面的代码,但略有不同:

public void animateArc(float fromAngle, float toAngle, long duration)
{
    sweepAngle = fromAngle;

    invalidate();

    // ---------------------------------------------------------------
    // Note: might want to change use absolute value for totalAngle
    // in case you want the animation to play backwards
    // ---------------------------------------------------------------
    float totalAngle = toAngle - fromAngle;

    updateArc(totalAngle, duration);
}

public void updateArc(final float totalAngle, final long durationInMilliSeconds)
{
    final long stepMilliseconds = 1;

    handler.postDelayed(new Runnable()
    {
        @Override
        public void run()
        {
            // ------------------------------------------------
            // 17790.0 is a number I fine tuned and came out
            // with on my Android phone to get the animation
            // as close as possible to the animation
            // duration specified
            // ------------------------------------------------
            double stepAngle = totalAngle / 1000.0 * (17790.0 / durationInMilliSeconds);

            sweepAngle += stepAngle;
            animationTime += stepMilliseconds;
            invalidate();

            if(animationTime < durationInMilliSeconds && sweepAngle < totalAngle)
            {
                updateArc(totalAngle, durationInMilliSeconds);
            }
            else
            {
                // --------------------------------------------------------
                // time duration reached, stop incrementing/decrementing
                // angle and reset animation time back to 0
                // --------------------------------------------------------
                animationTime = 0;
            }
        }
    }, 0);
}

【讨论】:

  • 我确实很努力地进入了“穷人”的领域。很好的答案,无法在我的脑海中拼凑起来。干得好!
【解决方案2】:

使用 Handler 无法保证您的消息准时到达,因此动画可能看起来很奇怪。

  • 如果你想保留你的drawArc代码,你可以使用PropertyAnimation。在 onAnimationUpdate() 方法中,您将使视图无效。 请注意,无效既不能保证同步。如果动画非常快,它可能会跳过一些帧。

  • 您还有另一个解决方案,Drawable Animation。创建动画所需的尽可能多的图像是一项枯燥的任务,但它很容易实现。

【讨论】:

  • 哇,我遇到了与 RiThBo 相同的问题,并实现了我自己的动画方法,该方法关闭了大约 8 毫秒,但是是的,按照这个答案,我能够使用 ObjectAnimator 来制作动画。就像我自己的自定义实现一样,它可能不准确,但我得到了使用 Object Animator 进行缓动或不缓动的好处,而不是使用我自己的自定义实现的线性动画。写起来也更短:P
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-04
  • 1970-01-01
  • 1970-01-01
  • 2012-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多