[英]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: 但是,此代码会产生一些奇怪的意外行为:
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.