我有一个自定义View,IndicatorView,它本质上是一个三角形,根据圆的指定角度定位自己,半径等于三角形的长度。三角形指向的角度经常更新,我想在这两个位置之间设置动画,类似于时钟上的手的移动方式。下面是我的自定义视图的图示(未按比例绘制或按比例绘制;根据Android视图坐标平面绘制):
在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();
}
}
});
}
}
但是,此代码会产生一些奇怪且意外的行为:
显然,在某个地方,我的计算必须是不正确的,但是,我很难找出错误的地方。 问题:是否有更有效的方法让我的自定义视图动画旋转到给定角度?如果我正确接近这个,我哪里错了?
答案 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找到。