Tween与Interpolator
Android使用的Animation代表抽象的动画类,它包括如下子类:
AlphaAnimation
|
透明度
|
ScaleAnimation
|
缩放
|
TranslateAnimation
|
位移
|
RotateAnimation
|
旋转
|
为控制动画期间补入帧数量,需要借组于Interpolator
Interpolator是一个接口,有一个必须实现的方法:float getInterpolation(float input),有如下几个实现类:
LinearInterpolator
|
均匀速度改变
|
AccelerateInterpolator
| 逐渐加速 |
AccelerateDecelerateInterpolator
|
中间加速
|
CycleInterpolator
|
动画循环播放特定次数,变化曲线按正弦曲线改变
|
DecelerateInterpolator
|
开始的地方改变速度快,让后开始减速
|
定义补间动画<set.../>元素支持一个android:interpolator属性,该属性的属性值可以指定为Android默认支持的Interceptor。
@android:anim/linear_interpolator
...
在程序中通过AnimationUtils得到代表补间动画的Animation之后,接下来就调用View的startAnimation(Animation anim)方法开始对该View执行动画了
实际上一般都会采用动画资源文件来定义补间动画
示例代码:
anim.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
< set xmlns:android = "http://schemas.android.com/apk/res/android"
android:interpolator = "@android:anim/linear_interpolator"
>
< scale android:fromXScale = "1.0"
android:toXScale = "0.01"
android:fromYScale = "1.0"
android:toYScale = "0.01"
android:pivotX = "50%"
android:pivotY = "50%"
android:fillAfter = "true"
android:duration = "3000" />
< alpha android:fromAlpha = "1"
android:toAlpha = "0.05"
android:duration = "3000" />
< rotate android:fromDegrees = "0"
android:toDegrees = "1800"
android:pivotX = "50%"
android:pivotY = "50%"
android:duration = "3000" />
< set xmlns:android = "http://schemas.android.com/apk/res/android"
android:interpolator = "@android:anim/linear_interpolator"
>
< scale android:fromXScale = "1.0"
android:toXScale = "0.01"
android:fromYScale = "1.0"
android:toYScale = "0.01"
android:pivotX = "50%"
android:pivotY = "50%"
android:fillAfter = "true"
android:duration = "3000" />
< alpha android:fromAlpha = "1"
android:toAlpha = "0.05"
android:duration = "3000" />
< rotate android:fromDegrees = "0"
android:toDegrees = "1800"
android:pivotX = "50%"
android:pivotY = "50%"
android:duration = "3000" />
</
set
>
reverse.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
< set android:interpolator = "@android:anim/linear_interpolator"
xmlns:android = "http://schemas.android.com/apk/res/android"
android:startOffset = "3000" >
< scale android:fromXScale = "0.01"
android:toXScale = "1"
android:fromYScale = "0.01"
android:toYScale = "1"
android:pivotX = "50%"
android:pivotY = "50%"
android:fillAfter = "true"
android:duration = "3000" />
< alpha android:fromAlpha = "0.05"
android:toAlpha = "1"
android:duration = "3000" />
< rotate android:fromDegrees = "1800"
android:toDegrees = "0"
android:pivotX = "50%"
android:pivotY = "50%"
android:duration = "3000" />
</ set >
< set android:interpolator = "@android:anim/linear_interpolator"
xmlns:android = "http://schemas.android.com/apk/res/android"
android:startOffset = "3000" >
< scale android:fromXScale = "0.01"
android:toXScale = "1"
android:fromYScale = "0.01"
android:toYScale = "1"
android:pivotX = "50%"
android:pivotY = "50%"
android:fillAfter = "true"
android:duration = "3000" />
< alpha android:fromAlpha = "0.05"
android:toAlpha = "1"
android:duration = "3000" />
< rotate android:fromDegrees = "1800"
android:toDegrees = "0"
android:pivotX = "50%"
android:pivotY = "50%"
android:duration = "3000" />
</ set >
main.java
public
class
TweenAnim
extends
ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. main );
final ImageView flower = (ImageView) findViewById(R.id. flower );
final Animation anim = AnimationUtils.loadAnimation( this , R.anim. anim );
anim.setFillAfter( true );
final Animation reverse = AnimationUtils.loadAnimation( this , R.anim. reverse );
reverse.setFillAfter( true );
Button bn = (Button) findViewById(R.id. bn );
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg. what == 0x123)
{
flower.startAnimation(reverse);
}
}
};
bn.setOnClickListener( new OnClickListener()
{
@Override
public void onClick(View arg0)
{
flower.startAnimation(anim);
new Timer().schedule( new TimerTask()
{
@Override
public void run()
{
handler.sendEmptyMessage(0x123);
}
},3500);
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. main );
final ImageView flower = (ImageView) findViewById(R.id. flower );
final Animation anim = AnimationUtils.loadAnimation( this , R.anim. anim );
anim.setFillAfter( true );
final Animation reverse = AnimationUtils.loadAnimation( this , R.anim. reverse );
reverse.setFillAfter( true );
Button bn = (Button) findViewById(R.id. bn );
final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg. what == 0x123)
{
flower.startAnimation(reverse);
}
}
};
bn.setOnClickListener( new OnClickListener()
{
@Override
public void onClick(View arg0)
{
flower.startAnimation(anim);
new Timer().schedule( new TimerTask()
{
@Override
public void run()
{
handler.sendEmptyMessage(0x123);
}
},3500);
}
});
}
}
另一个示例
(蝴蝶飞行时的效果是逐帧动画;蝴蝶飞行时位置改变是补间动画)
butterfly.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
< animation-list xmlns:android = "http://schemas.android.com/apk/res/android"
android:oneshot = "false" >
< item android:drawable = "@drawable/butterfly_f01" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f02" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f03" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f04" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f05" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f06" android:duration = "120" />
< animation-list xmlns:android = "http://schemas.android.com/apk/res/android"
android:oneshot = "false" >
< item android:drawable = "@drawable/butterfly_f01" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f02" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f03" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f04" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f05" android:duration = "120" />
< item android:drawable = "@drawable/butterfly_f06" android:duration = "120" />
</
animation-list
>
main.xml
<
RelativeLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
xmlns:tools = "http://schemas.android.com/tools"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:paddingBottom = "@dimen/activity_vertical_margin"
android:paddingLeft = "@dimen/activity_horizontal_margin"
android:paddingRight = "@dimen/activity_horizontal_margin"
android:paddingTop = "@dimen/activity_vertical_margin"
tools:context = "com.example.butterfly.Butterfly" >
< ImageView
android:id = "@+id/butterfly"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:background = "@anim/butterfly" />
</ RelativeLayout >
xmlns:tools = "http://schemas.android.com/tools"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
android:paddingBottom = "@dimen/activity_vertical_margin"
android:paddingLeft = "@dimen/activity_horizontal_margin"
android:paddingRight = "@dimen/activity_horizontal_margin"
android:paddingTop = "@dimen/activity_vertical_margin"
tools:context = "com.example.butterfly.Butterfly" >
< ImageView
android:id = "@+id/butterfly"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:background = "@anim/butterfly" />
</ RelativeLayout >
main.java
public
class
Butterfly
extends
ActionBarActivity {
private float curX = 0;
private float curY = 0;
float nextX = 0;
float nextY = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. main );
final ImageView imageView = (ImageView)
findViewById(R.id. butterfly );
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg)
{
if (msg. what == 0x123)
{
if ( nextX > 480)
{
curX = nextX = 0;
}
else
{
nextX += 8;
}
nextY = curY + ( float ) (Math.random() * 10 -5);
TranslateAnimation anim = new TranslateAnimation( curX , nextX , curY , nextY );
curX = nextX ;
curY = nextY ;
anim.setDuration(200);
imageView.startAnimation(anim);
}
}
};
final AnimationDrawable butterfly = (AnimationDrawable)
imageView.getBackground();
imageView.setOnClickListener( new OnClickListener()
{
@Override
public void onClick(View v)
{
butterfly.start();
new Timer().schedule( new TimerTask()
{
@Override
public void run()
{
handler.sendEmptyMessage(0x123);
}
},0,200);
}
});
private float curX = 0;
private float curY = 0;
float nextX = 0;
float nextY = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. main );
final ImageView imageView = (ImageView)
findViewById(R.id. butterfly );
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg)
{
if (msg. what == 0x123)
{
if ( nextX > 480)
{
curX = nextX = 0;
}
else
{
nextX += 8;
}
nextY = curY + ( float ) (Math.random() * 10 -5);
TranslateAnimation anim = new TranslateAnimation( curX , nextX , curY , nextY );
curX = nextX ;
curY = nextY ;
anim.setDuration(200);
imageView.startAnimation(anim);
}
}
};
final AnimationDrawable butterfly = (AnimationDrawable)
imageView.getBackground();
imageView.setOnClickListener( new OnClickListener()
{
@Override
public void onClick(View v)
{
butterfly.start();
new Timer().schedule( new TimerTask()
{
@Override
public void run()
{
handler.sendEmptyMessage(0x123);
}
},0,200);
}
});
}
}
自定义补间动画
Android提供了Animation作为补间动画抽象基类,而且有AlphaAnimation、RotateAnimation、ScaleAnimation、TranslateAnimation四个实现类(补间动画的四种形式:透明度改变、旋转、缩放、位移),实际项目中可能还需要一些更复杂的动画,如让图片在“三维”空间内进行旋转动画等,这就需要开发者自己开发补间动画了
自定义补间动画并不难,需要继承Animation,关键是要重写该抽象基类的
applyTransformation(float interpolatedTime, Transformation t)
interpolatedTime
|
代表动画的进行时间比(由0变化到1)
|
Transformation
|
代表了补间动画在不同时刻对图形或组件的变形程度
|
Transformation代表了对图片或试图的变形,该对象里封装了一个Matrix对象,对它所包装的Matrix进行位移、倾斜、旋转等变形;
为了控制图片或View进行三维空间的变换,还需借助于Android提供的Camera,此Camera并非摄像头,只是一个空间变换工具,作用类似于Matrix,但功能更强大
Camera提供如下常用方法
getMatrix(Matrix matrix)
|
将Camera所做的变换应用到指定matrix上
|
rotateX(float deg)
|
沿X轴旋转
|
rotateY(float deg)
|
沿Y轴旋转
|
rotateZ(float deg)
|
沿Z轴旋转
|
translate(float x, float y, float z)
|
把目标组件在三维空间里进行变换
|
applyToCanvas(Canvas canvas)
|
把Camera所做的变换应用到Canvas上
|
当Camera控制图片或View沿X、Y或Z轴旋转时,被旋转的图片或View将呈现三维透视效果
示例代码
main.xml
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
>
< ListView
android:id = "@+id/list"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:entries = "@array/bookArray" />
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
>
< ListView
android:id = "@+id/list"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:entries = "@array/bookArray" />
</LinearLayout>
MyAnimation.java
public
class
MyAnimation
extends
Animation{
private float centerX ;
private float centerY ;
private int duration ;
private Camera camera = new Camera();
public MyAnimation( float x, float y, int duration)
{
this . centerX = x;
this . centerY = y;
this . duration = duration;
}
@Override
public void initialize( int width, int height
, int parentWidth, int parentHeight)
{
super .initialize(width,height, parentWidth, parentHeight);
setDuration( duration );
setFillAfter( true );
setInterpolator( new LinearInterpolator());
}
@Override
protected void applyTransformation( float interpolatedTime, Transformation t)
{
camera .save();
camera .translate(100.0f - 100.0f*interpolatedTime
, 150.0f * interpolatedTime - 150
, 80.0f - 80.0f*interpolatedTime);
camera .rotateY(360*(interpolatedTime));
camera .rotateX((360*interpolatedTime));
Matrix matrix = t.getMatrix();
camera .getMatrix(matrix);
matrix.preTranslate(- centerX , - centerY );
matrix.postTranslate( centerX , centerY );
camera .restore();
}
private float centerX ;
private float centerY ;
private int duration ;
private Camera camera = new Camera();
public MyAnimation( float x, float y, int duration)
{
this . centerX = x;
this . centerY = y;
this . duration = duration;
}
@Override
public void initialize( int width, int height
, int parentWidth, int parentHeight)
{
super .initialize(width,height, parentWidth, parentHeight);
setDuration( duration );
setFillAfter( true );
setInterpolator( new LinearInterpolator());
}
@Override
protected void applyTransformation( float interpolatedTime, Transformation t)
{
camera .save();
camera .translate(100.0f - 100.0f*interpolatedTime
, 150.0f * interpolatedTime - 150
, 80.0f - 80.0f*interpolatedTime);
camera .rotateY(360*(interpolatedTime));
camera .rotateX((360*interpolatedTime));
Matrix matrix = t.getMatrix();
camera .getMatrix(matrix);
matrix.preTranslate(- centerX , - centerY );
matrix.postTranslate( centerX , centerY );
camera .restore();
}
}
main.java
public
class
ListViewTween
extends
ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. main );
ListView list = (ListView) findViewById(R.id. list );
WindowManager windowManager = (WindowManager)
getSystemService( WINDOW_SERVICE );
Display display = windowManager.getDefaultDisplay();
DisplayMetrics metrice = new DisplayMetrics();
display.getMetrics(metrice);
list.setAnimation( new MyAnimation(metrice. xdpi / 2
,metrice. ydpi /2,3500 ));
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. main );
ListView list = (ListView) findViewById(R.id. list );
WindowManager windowManager = (WindowManager)
getSystemService( WINDOW_SERVICE );
Display display = windowManager.getDefaultDisplay();
DisplayMetrics metrice = new DisplayMetrics();
display.getMetrics(metrice);
list.setAnimation( new MyAnimation(metrice. xdpi / 2
,metrice. ydpi /2,3500 ));
}
}