2016-01-22 31 views
1

我有一個自定義的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並使用從動畫中指定的中間值叫我showAngle()方法在我IndicatorView:

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

所以,我的問題的解決方案很簡單,但很容易被忽略。用於計算的角度場以度爲單位,只需將其轉換爲弧度,以便它能夠使用正弦和餘弦方法。

更改所有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上找到。