简体   繁体   English

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

[英]Rotation animation with custom view

I have a custom View, IndicatorView, which is essentially a triangle that orients itself according to a specified angle of a circle with a radius equal to the triangle's length. 我有一个自定义的View,IndicatorView,它本质上是一个三角形,它根据半径等于三角形长度的圆的指定角度对自身进行定向。 The angle the triangle points to is frequently updated and I would like to animate between these two positions similar to how a hand on a clock moves. 三角形指向的角度经常更新,我想在这两个位置之间设置动画,类似于时钟上的指针如何移动。 Below is an illustration of my custom view (not drawn proportionally or to scale; drawn according to the Android View coordinate plane): 以下是我的自定义视图的图示(未按比例绘制或按比例绘制;根据Android View坐标平面绘制):

在此处输入图片说明

In the IndicatorView class, I draw the triangle using a Path object and three PointF objects: 在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);
}

To calculate the different points, given the angle, I use parametric equations: 为了计算给定角度的不同点,我使用参数方程式:

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();
}

When I have a new angle, I use an ObjectAnimator to animate between the two angles. 当我有一个新角度时,我使用ObjectAnimator在两个角度之间设置动画。 I place an AnimatorUpdateListener on the ObjectAnimator and call my showAngle() method in my IndicatorView using the intermediate values specified from the Animator: 我将AnimatorUpdateListener放在ObjectAnimator上,并使用从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();
                }
            }
        });
    }
}

However, this code produces some strange and unexpected behavior: 但是,此代码会产生一些奇怪的意外行为:

  • The width size of the triangle changes somewhat drastically. 三角形的宽度大小会急剧变化。 This could be due to casting between different types but it shouldn't be that dramatic. 这可能是由于不同类型之间的转换引起的,但不应该那么引人注目。
  • The point of the triangle never stops at the specified angle. 三角形的点永远不会停在指定的角度。 Instead it just keeps moving in a circle. 相反,它只是绕圈运动。
  • The angle seems to dictate the animations speed rather than where the triangle should stop. 角度似乎决定了动画的速度,而不是三角形应停在的位置。
  • Sometimes it seems as though there are numerous triangles on the screen. 有时似乎屏幕上有许多三角形。 This could be due to the speed, perhaps it's moving very fast. 这可能是由于速度所致,也许是移动速度非常快。

Obviously, somewhere along the line my calculations must be incorrect, though, I'm struggling to find out where I went wrong. 显然,尽管如此,我的计算肯定是不正确的,但我一直在努力找出哪里出了问题。 Question(s): Is there a more efficient way of getting my custom view to animate rotation to a given angle? 问题:是否有更有效的方法来使我的自定义视图对旋转到给定角度进行动画处理? If I am approaching this correctly, where am I going wrong? 如果我正确地解决了这个问题,那我哪里出错了?

So, the solution to my problem was rather simple but simple enough to be overlooked. 因此,解决我的问题的方法相当简单,但又足够容易被忽略。 The angle field that was being used for the calculations was in degrees and it just had to be converted to radians in order for it to work with the sin and cos methods. 计算中使用的角度场以度为单位,只需将其转换为弧度即可使用sin和cos方法。

Change all PointF instantiations, for instance: 更改所有PointF实例,例如:

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

to use the angle in radians: 使用弧度角:

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

Also, part of the problem was due to sound constantly being analyzed and the View being updated before the previous animation had time to render a few frames. 另外,部分问题是由于不断分析声音并在上一个动画有时间渲染几帧之前更新了视图。 This led to the IndicatorView hardly moving when the angle was being updated often and when it was not it would quickly move to its destination. 这导致在频繁更新角度时,IndicatorView几乎不动,而在不更新角度时,它会迅速移至其目的地。 This happens because the previous animation is canceled before another animation is set (which is necessary to prevent a delay). 发生这种情况是因为在设置另一个动画之前,先前的动画被取消了(这是防止延迟的必要步骤)。 This is a tricky problem to fix but one optimization I found was to avoid starting a new animation if the current angle and the previous angle were relatively close to each other. 这是一个棘手的问题,但是我发现一个优化是避免在当前角度和前一个角度相对接近的情况下避免启动新动画。

Hopefully this will be useful for someone stuck with a similar problem. 希望这对遇到类似问题的人有用。 This was all part of a guitar tuner project I was working on and the source can be found on GitHub . 这是我正在从事的吉他调音器项目的全部内容,其源代码可以在GitHub找到

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM