具有自定义视图的旋转动画

时间:2016-01-22 03:03:45

标签: java android animation

我有一个自定义View,IndicatorView,它本质上是一个三角形,根据圆的指定角度定位自己,半径等于三角形的长度。三角形指向的角度经常更新,我想在这两个位置之间设置动画,类似于时钟上的手的移动方式。下面是我的自定义视图的图示(未按比例绘制或按比例绘制;根据Android视图坐标平面绘制):

enter image description here

在IndicatorView类中,我使用Path对象和三个PointF对象绘制三角形:

@Override
protected void onDraw(Canvas canvas){
    path = new Path();
    path.setFillType(Path.FillType.EVEN_ODD);
    //a, b, and c are PointF objects
    path.moveTo(a.x, a.y);
    path.lineTo(b.x, b.y);
    path.lineTo(c.x, c.y);
    path.close();
    canvas.drawPath(path, paint);
}

为了计算不同的点,给定角度,我使用参数方程:

public void showAngle(){
    //x = centerX + radius * cos(angle)
    //y = centerY + radius * sin(angle)
    //TODO sloppy; tidy up / optimize once finished
    //centerX, centerY, length, and bottomWidth are all values
    //calculated in onSizeChanged
    a = new PointF((float) (centerX + (length * Math.cos(angle))), (float) (centerY + (length * Math.sin(angle))));
    //perpendicular bilateral radius
    double pRadius = bottomWidth / 2;
    //perpendicular angle plus or minus 90 degrees depending on point
    float pAngle = angle - 90;
    pAngle = (pAngle < 0) ? 360 - Math.abs(pAngle) : pAngle;
    pAngle = (pAngle > 360) ? pAngle % 360 : pAngle;
    b = new PointF((float) (centerX + (pRadius * Math.cos(pAngle))), (float) (centerY + (pRadius * Math.sin(pAngle))));
    pAngle = angle + 90;
    pAngle = (pAngle < 0) ? 360 - Math.abs(pAngle) : pAngle;
    pAngle = (pAngle > 360) ? pAngle % 360 : pAngle;
    c = new PointF((float) (centerX + (pRadius * Math.cos(pAngle))), (float) (centerY + pRadius * Math.sin(pAngle)));
    invalidate();
}

当我有一个新角度时,我使用ObjectAnimator在两个角度之间进行动画处理。我在ObjectAnimator上放置一个AnimatorUpdateListener,并使用Animator指定的中间值在我的IndicatorView中调用我的showAngle()方法:

public void updateAngle(float newAngle){
    //don't animate to an angle if the previous angle is the same
    if(view.getAngle() != newAngle){
        if(anim != null && anim.isRunning()){
            anim.cancel();
        }
        anim = ObjectAnimator.ofFloat(view, "angle", view.getAngle(), newAngle);
        anim.setDuration(duration);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if(view != null){
                    view.showAngle();
                }
            }
        });
    }
}

但是,此代码会产生一些奇怪且意外的行为:

  • 三角形的宽度尺寸略有变化。这可能是由于不同类型之间的投射,但它不应该那么戏剧性。
  • 三角形的点永远不会停在指定的角度。相反,它只是继续围成一圈。
  • 角度似乎决定了动画的速度,而不是三角形应停止的位置。
  • 有时似乎屏幕上有很多三角形。这可能是由于速度,也许它的移动速度非常快。

显然,在某个地方,我的计算必须是不正确的,但是,我很难找出错误的地方。 问题:是否有更有效的方法让我的自定义视图动画旋转到给定角度?如果我正确接近这个,我哪里错了?

1 个答案:

答案 0 :(得分:0)

所以,我的问题的解决方案相当简单,但很容易被忽视。用于计算的角度字段以度为单位,只需将其转换为弧度即可使其与sin和cos方法一起使用。

更改所有PointF实例,例如:

a = new PointF((float) (centerX + (length * Math.cos(angle))), (float) (centerY + (length * Math.sin(angle))));

以弧度为单位使用角度:

a = new PointF((float) (centerX + (length * Math.cos(Math.toRadians(angle))),
        (float) (centerY + (length * Math.sin(Math.toRadians(angle)))));

此外,部分问题是由于声音不断被分析,并且在上一个动画有时间渲染几帧之前视图正在更新。这导致当角度经常更新时,IndicatorView几乎不移动,而当它不经常更新时,它会迅速移动到目的地。发生这种情况是因为在设置另一个动画之前取消了前一个动画(这是防止延迟所必需的)。这是一个棘手的问题,我找到的一个优化是避免在当前角度和前一个角度彼此相对接近时开始新动画。

希望这对遇到类似问题的人有用。这是我正在研究的吉他调谐器项目的一部分,源代码可以在GitHub找到。