Android动画详解
帧动画
所谓帧动画其实很好理解,就是通过在一定的时间间隔内,将一组图片顺序播放出来,从而打到动画的效果,我们通过简单的帧动画例子来说明一下。
下面是帧动画的xml资源代码,animation-list就是帧动画的标签,它包含的item就是每一帧的图像。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@mipmap/one"
android:duration="500"/>
<item
android:drawable="@mipmap/two"
android:duration="500"/>
<item
android:drawable="@mipmap/three"
android:duration="500"/>
<item
android:drawable="@mipmap/four"
android:duration="500"/>
<item
android:drawable="@mipmap/five"
android:duration="50"/>
<item
android:drawable="@mipmap/six"
android:duration="50"/>
</animation-list>
接下来我们创建帧动画例子activity的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="开始"
android:id="@+id/start"
android:layout_marginTop="20dp"
android:onClick="onStart"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:id="@+id/stop"
android:text="停止"
android:onClick="onStop"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop ="20dp"
android:id="@+id/imageView"/>
</LinearLayout>
然后是activity里面的代码
public class FrameAnimationActivity extends AppCompatActivity {
private AnimationDrawable animationDrawable; //此处写错了 写成了Animation
private ImageView imageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frame_activity);
init();
}
private void init(){
imageView = (ImageView)findViewById(R.id.imageView);
imageView.setBackgroundResource(R.drawable.frame_anim);
animationDrawable = (AnimationDrawable) imageView.getBackground();
}
public void onStart(View view){
if(animationDrawable != null && !animationDrawable.isRunning()){
animationDrawable.start();
}
}
public void onStop(View view){
if(animationDrawable != null && animationDrawable.isRunning()){
animationDrawable.stop();
}
}
}
通过上面的代码可以知道,帧动画主要需要记住3点,1是动画资源文件-drawable里面的文件,animation-list以及它的子标签item。2是AnimationDrawable这个类以及这个类的实例对象通过imageView控件的getBackground方法得到的。3是AnimationDrawable的用法(start()、stop()这个几个方法)。
补间动画
补间动画主要有四种操作:透明变化、缩放控制、旋转控制、移动控制。这四种动画变化都是通过资源文件来实现的,我们在res文件夹里面创建anim文件夹。然后根据需要创建相应的xml文件,这里通过一个例子说明用法。
下面是alpha、scale、rotate、translate、set对应的xml文件,set文件是将上面所有动画种类选择性地集合在一起,同时执行。
- alpha.xml
<?xml version="1.0" encoding="utf-8" ?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromAlpha="0"
android:toAlpha="1"
android:duration="4000"/>
<!-- interpolator:表示采用这个算法,设置动画播放的平缓的程度 -->
<!-- fromAlpha:初始透明度-->
<!-- toAlpha:最终透明度-->
- scale.xml
<?xml version="1.0" encoding="utf-8" ?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromYScale="0"
android:fromXScale="0"
android:toXScale="1"
android:toYScale="1"
android:pivotY="0"
android:pivotX="100%"
android:duration="3000"/>
<!-- 缩放的的基点 pivotX和pivotY 如果在百分号数值后面加一个p,就是控件左上角加上父控件百分之多少的值-->
<!-- fromYScale:初始Y轴的缩放比例-->
<!-- fromXScale:初始X轴的缩放比例-->
<!-- toYScale:最终Y轴的缩放比例-->
<!-- toXScale:最终X轴的缩放比例-->
- rotate.xml
<?xml version="1.0" encoding="utf-8" ?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="30%"
android:fromDegrees="0"
android:toDegrees="360"
android:duration="3000"/>
<!-- 旋转的的基点 pivotX和pivotY 如果在百分号数值后面加一个p,就是控件左上角加上父控件百分之多少的值-->
- translate.xml
<?xml version="1.0" encoding="utf-8" ?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXDelta="0"
android:fromYDelta="0"
android:toYDelta="500"
android:toXDelta="500"
android:duration="3000"/>
- set.xml
<?xml version="1.0" encoding="utf-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:shareInterpolator="true">
<scale
android:pivotY="50%"
android:pivotX="50%"
android:fromXScale="0"
android:fromYScale="0"
android:toYScale="1"
android:toXScale="1"
android:duration="1000"/>
<!-- 会覆盖前面的scale动画-->
<scale
android:pivotY="50%"
android:pivotX="50%"
android:fromXScale="1"
android:fromYScale="1"
android:toYScale="0"
android:toXScale="0"
android:duration="1000"/>
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:duration="1000"/>
</set>
补间动画的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="透明动画"
android:onClick="onAlpha"
android:layout_marginTop="20dp"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:onClick="onScale"
android:text="缩放动画"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginTop="20dp"
android:onClick="onRotate"
android:text="旋转动画"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginTop="20dp"
android:onClick="onTranslate"
android:text="位移动画"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:onClick="onSet"
android:text="动画组合"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="20dp"
android:layout_gravity="center_horizontal"
android:id="@+id/imageView"
android:background="@mipmap/five"/>
</LinearLayout>
activity代码
public class TweenAnimationActivity extends AppCompatActivity {
private Animation animation;
private ImageView imageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tween_activity);
init();
}
private void init(){
imageView = (ImageView)findViewById(R.id.imageView);
}
public void onAlpha(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.alpha);
imageView.startAnimation(animation);
}
public void onScale(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.scale);
imageView.startAnimation(animation);
}
public void onRotate(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.rotate);
imageView.startAnimation(animation);
}
public void onTranslate(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.translate);
imageView.startAnimation(animation);
}
/**
*
* @param view
*
* 对于set的理解就是,set.xml文件里面定义这四种动画,然后同时执行,也可以定义一样的动画类型,但是会覆盖前面的同类型动。
*/
public void onSet(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.set);
imageView.startAnimation(animation);
}
}
通过上面示例代码可以看出。补间动画主要记住3点,1是补间动画资源文件的写法,2是Animation类和AnimationUtils类loadAnimation()方法的用法, 3是插值器,参考这篇文章。
属性动画
属性动画需要和补间动画区分开来,因为前者的变化是真实的,后者的变化是不真实的,补间动画只是给人一种障眼法,比如当补间动画移动之后,你点击它的显示区域,发现不会响应点击事件,只有点击它的原始区域才会响应点击事件。而属性动画就不一样了,只要移动在屏幕可显示区域就可以响应点击事件。同样地给出一个例子来说明。
- 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:onClick="onAlpha"
android:text="透明"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:onClick="onScale"
android:text="缩放"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:onClick="onRotate"
android:text="旋转"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:onClick="onTranslate"
android:text="移动"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onSet"
android:text="集合"/>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="30dp"
android:layout_gravity="center_horizontal"
android:text="我是移动的控件"
android:id="@+id/move_button"/>
</LinearLayout>
- activity代码
public class AttributeAnimationActivity extends AppCompatActivity {
private ObjectAnimator objectAnimator;
private Button show_button;
private final static String TAG = "提示";
private AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator rotateObjectAnimator1 = new ObjectAnimator();
ObjectAnimator translateObjectAnimator1 = new ObjectAnimator();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.attribute_activity);
init();
animatorSet.addListener(new AnimatorListenerAdapter() {
private AnimatorSet animatorSet2 = new AnimatorSet();
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
// animation.cancel();
Log.d(TAG, "onAnimationEnd: 动画完成,开始回来动画");
ObjectAnimator rotateObjectAnimator2 = new ObjectAnimator();
ObjectAnimator translateObjectAnimator2 = new ObjectAnimator();
rotateObjectAnimator2 = rotateObjectAnimator2.ofFloat(show_button, "rotation", 0f, 360f);
translateObjectAnimator2 = translateObjectAnimator2.ofFloat(show_button, "translationY", 200f, 150f, 100f, 50f, 0f);
animatorSet2.setDuration(2000);
animatorSet2.play(rotateObjectAnimator2).with(translateObjectAnimator2);
animatorSet2.start();
}
});
}
private void init(){
show_button = (Button)findViewById(R.id.move_button);
}
public void onAlpha(View view){
Log.d(TAG, "onAlpha: ");
//value是不定长形参数组。里面的值代表动画的变化过程值。
objectAnimator = objectAnimator.ofFloat(show_button, "alpha", 1f, 0.5f, 0f, 0.5f, 1f);
objectAnimator.setDuration(2000);
objectAnimator.start();
}
public void onScale(View view){
Log.d(TAG, "onScale: ");
objectAnimator = objectAnimator.ofFloat(show_button, "scaleY", 0f, 1f, 2f, 1f);
objectAnimator.setDuration(2000);
objectAnimator.start();
}
public void onRotate(View view){
Log.d(TAG, "onRotate: ");
objectAnimator = objectAnimator.ofFloat(show_button, "rotation", 0f, 90f, 360f);
objectAnimator.setDuration(2000);
objectAnimator.setRepeatCount(3);
objectAnimator.setRepeatMode(ValueAnimator.RESTART);
objectAnimator.start();
}
public void onTranslate(View view){
Log.d(TAG, "onTranslate: ");
objectAnimator = objectAnimator.ofFloat(show_button, "translationY", 0f, 50f, 100f, 150f, 200f);
objectAnimator.setDuration(2000);
objectAnimator.start();
}
public void onSet(View view){
Log.d(TAG, "onSet: ");
rotateObjectAnimator1 = rotateObjectAnimator1.ofFloat(show_button, "rotation", 0f, 90f, 360f);
translateObjectAnimator1 = translateObjectAnimator1.ofFloat(show_button, "translationY", 0f, 50f, 100f, 150f, 200f);
animatorSet.setDuration(2000);
animatorSet.play(rotateObjectAnimator1).with(translateObjectAnimator1);
animatorSet.start();
}
}
属性动画需要记住3点,1是属性动画不依赖xml文件。2是ObjectAnimator类以及它的ofFloat()方法的用法。3是AnimatorSet类的用法。
总结
Android动画类型从简单到复杂是,帧动画-补间动画-属性动画。同时灵活程度也是如此,帧动画可以实现一些简单的显示效果,如果想要移动、旋转等操作就需要用到补间动画了,而如果要实现是跟为复杂或者可响应点击事件的功能,那就只能选择属性动画了。