package com.android_mvc.framework.ui.anim.desc;
import java.util.ArrayList;
import java.util.List;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
/**
* 円弧上の移動アニメーションの記述を簡素化するためのクラス。
* @author id:language_and_engineering
*
*/
public class ArcAnimationDescription extends AnimationDescription {
// NOTE: RotateAnimationの挙動が謎で,
// 一度でも使ってしまうと補正が複雑で対処できなくなるため,代わりに作成。
// TODO: 円弧の中心点に「幻像」が表示されてしまう,という謎のバグがある。
private int from_theta;
private int diff_theta;
private float radius;
private int diff_theta_direction;
private float last_x;
private float last_y;
// 円弧を何度ずつの直線が集まった多角形とみなすか?
private int delta_theta = 15;
/**
* 移動アニメーションの初期化。
* 開始角,差分角,半径を指定。
* 角度は度数法で,x軸から反時計回りに数える。
* 開始角は0~360であること。
*/
public ArcAnimationDescription(int from_theta, int diff_theta, float radius)
{
this.from_theta = from_theta % 360;
this.diff_theta = diff_theta;
this.radius = radius;
this.diff_theta_direction = ( diff_theta >= 0 ) ? +1 : -1;
}
/**
* 角度を指定して,円弧を近似する多角形の形状を決定。
*/
public ArcAnimationDescription deltaDegree( int theta )
{
this.delta_theta = theta;
return this;
}
@Override
protected List<Animation> describeAsList()
{
List<Animation> anims = new ArrayList<Animation>();
// 該当角度までの断片的な直線移動を積み重ねる。
// 方向付きデルタ
int delta_theta_with_direction = delta_theta * diff_theta_direction;
// 移動軸タイプ
int axis_type = Animation.ABSOLUTE;
// 移動の中心座標。開始地点を原点とする。
double theta_center = ( 180 + from_theta ) * 2 * Math.PI / 360f;
int center_x = + (int)( radius * Math.cos( theta_center ) );
int center_y = - (int)( radius * Math.sin( theta_center ) ); // NOTE:下向きが正なので負号が付く
// 繰り返す
int moved_theta_sum = 0;
int current_theta = from_theta;
boolean continue_flag = true;
Animation anim;
float fromXValue;
float fromYValue;
float toXValue = 0;
float toYValue = 0;
while( continue_flag )
{
// 移動先の角度
int next_theta = current_theta + delta_theta_with_direction;
// 座標を算出
fromXValue = (float)( center_x + radius * Math.cos( current_theta * 2 * Math.PI / 360f ) );
fromYValue = (float)( center_y - radius * Math.sin( current_theta * 2 * Math.PI / 360f ) );
toXValue = (float)( center_x + radius * Math.cos( next_theta * 2 * Math.PI / 360f ) );
toYValue = (float)( center_y - radius * Math.sin( next_theta * 2 * Math.PI / 360f ) );
// アニメーションとして追加登録
anim = new TranslateAnimation(
axis_type, fromXValue,
axis_type, toXValue,
axis_type, fromYValue,
axis_type, toYValue
);
anim.setInterpolator(new LinearInterpolator()); // これがないとデルタ毎にいちいち速度が変化する
anims.add(anim);
// 継続判定
current_theta = next_theta;
moved_theta_sum += delta_theta_with_direction;
if( Math.abs(diff_theta) <= Math.abs(moved_theta_sum) )
{
continue_flag = false;
}
}
// 最後の座標を補完
this.last_x = toXValue;
this.last_y = toYValue;
return anims;
}
@Override
protected void modifyAfterAnimation(View v)
{
// 最終的な移動場所をmarginとして加減
modifyMarginsOfOneView( v, this.last_x, this.last_y );
}
}