[0 to 0.5]从零开始学习Android动画知识(上)
为什么要学习动画
当然是因为COOL啊用户对产品的体验是由多种要素构成的。
在APP开发过程中,影响产品最终形象的元素有很多,而动画效果一直是其中不可或缺的一部分。
好的动画效果不仅可以在单一界面中容纳更多信息,传达并反馈更多的状态 (更炫酷);
还可以引导用户对产品的交互,促进用户对信息的理解,使用产品时更加流畅 (更简单)。
动画框架
Android的动画本来有两种:补间动画(Tween Animation)和逐帧动画(Frame Animation),并统称为视图动画(View Animation),在Android3.0(API 11)之后又加入了属性动画 (Property Animation)。
Animation
View Animation
Property Animation
Frame Animation
Tween Animation
视图动画(View Animation)
逐帧动画(Frame Animation)
简单来说,逐帧动画就是将一组预先准备好的图片通过控制依次显示并循环播放(参考幻灯片),从而造成一种动画效果。
通常逐帧动画有两种实现方式: XML 和 Java 。
用XML方法实现
首先,创建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/type1" android:duration="150" />
<item android:drawable="@drawable/type2" android:duration="150" />
</animation-list>
- android:oneshot 代表是否循环(true 为不循环,false 为循环)
- drawable代表加入的图片素材,而duration代表该素材一帧的持续时长(ms)
然后,将上述XML文件(frame.xml)作为ImageView(image)的图像,并调用AnimationDrawable类获取该图像生成的实例
private ActivityMainBinding binding;
private AnimationDrawable frame;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.image.setImageResource(getResources().getIdentifier("frame","drawable",getPackageName()));
frame = (AnimationDrawable) binding.image.getDrawable();//获取实例
binding.Start.setOnClickListener(view -> {frame.start();});
binding.Stop.setOnClickListener(view -> {frame.stop();});
}
效果如下:
[查看图片]
用Java方法实现
Java代码实现逐帧动画和xml类似,相当于使用Java代码获取AnimationDrawable实例
private ActivityMainBinding binding;
private AnimationDrawable frame;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
frame = new AnimationDrawable();
for (int i = 1 ,id;i <= 2; i ++ ) {
id = getResources().getIdentifier("type" + i, "drawable", getPackageName());
frame.addFrame(getResources().getDrawable(id),150);
}//相当于创建frame.xml
frame.setOneShot(false);//设置是否循环
binding.image.setImageDrawable(frame);
binding.Start.setOnClickListener(view -> {frame.start();});
binding.Stop.setOnClickListener(view -> {frame.stop();});
}
逐帧动画的优缺点
优点:代码量小,使用较为方便、简单
缺点:动画的变化基于图片的切换,越是流畅的动画越需要更多的图片,从而占用了大量内存。所以,使用逐帧动画时一定要注意图片的大小和数目!
补间动画(Tween Animation)
补间动画是通过已确定的开始视图样式和结束视图样式,并由系统计算来补全其中间动画变化过程的动画
补间动画同样支持XML和Java两种方法,这里我推荐使用更易读、可切换、可重复使用的XML方法
Android支持的补间动画效果有以下4种:旋转(rotate)、位移(translate)、缩放(scale)和透明度(alpha)
动画名 | 对于Animation子类 |
| 动画效果 |
缩放 | ScaleAnimation |
| 进行特定范围的缩放 |
透明度 | AlphaAnimation |
| 改变透明度,实现隐现 |
位移 | TranslateAnimation |
| 进行位置的移动 |
旋转 | RotateAnimation |
| 围绕特定的点进行旋转 |
复合 | AnimationSet |
| 复合使用上述四种效果 |
上述动画的类均继承自Animation类,因此存在一些通用的动画属性和方法
xml属性对应Java方法效果
android:duration
setDuration(long)
动画持续时间(ms)android:fillAfter
setFillAfter(boolean)
保持动画结束时的状态,默认tureandroid:fillBefore
setFillBefore(boolean)
还原到动画开始时的状态,默认为falseandroid:repeatCount
setRepeatCount(int)
动画重复执行的次数android:repeatMode
setRepeatMode(int)
动画重复模式 ,restart从头开始,reverse倒叙回放android:startOffset
setStartOffset(long)
动画开始前延迟的时间(ms)android:interpolator
setInterpolator(Interpolator)
插值器,改变动画的不同阶段的执行速度
这里的插值器(interpolator)在属性动画中较为常用,我们将在后面细说。
透明度动画(Alpha)
xml方法代码
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromAlpha="1.0"
android:toAlpha="0.0"
/>
animation = AnimationUtils.loadAnimation(this,R.anim.alpha_anim);
对应Java方法代码
animation = new AlphaAnimation(1,0);
animation.setDuration(4000);
binding.tween.startAnimation(animation);
这里的
AlphaAnimation(float,float)
的两个值域为 [0,1] 的参数分别对应XML中的
android:fromAlpha
(初始透明度) 和android:toAlpha
(终止透明度)且值越接近0,透明度越高
效果如下:
[查看图片]
旋转动画(Rotate)
xml方法代码
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="180"
android:pivotY="50%"
android:pivotX="50%"
android:duration="2000"
/>
对应Java方法代码
animation = new RotateAnimation(0,180,Animation.RELATIVE_TO_SELF,0.50f,Animation.RELATIVE_TO_SELF,0.50f);
//RotateAnimation(fromDegrees,toDegrees,pivotXType,pivotX,pivotYType,pivotY)
animation.setDuration(1000);
显然,
RotataAnimation()
的前两个参数分别对应android:fromDegrees
(起始角度) 和android:toDegrees
(最终角度)而这里多出来的两个常数参数 pivotXType和pivotYType分别指定了如何去解释pivotX和pivotY
Animation.ABSOLUTE | Animation.RELATIVE_TO_SELF | Animation.RELATIVE_TO_PARENT |
指定尺寸是一个绝对像素数量。使用之后pivotX 或pivotY 的值都是绝对像素数量(px)。 | 指定尺寸是一个浮点数,用这个浮点数来乘动画对象的宽或高。如使用之后动画最终的x轴 =(pivotX 的值) * (动画对象的宽),即百分数 | 指定尺寸是一个浮点数,用这个浮点数来乘动画父对象的宽或高。如使用之后动画最终的x轴 =(pivotX 的值) * (动画父对象的宽),即百分数 |
效果如下:
[查看图片]
缩放动画(Scale)
xml方法代码
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0"
/>
对应Java方法代码
animation = new ScaleAnimation(0,1,0,1,Animation.RELATIVE_TO_SELF,0.50f,Animation.RELATIVE_TO_SELF,0.50f);
//ScaleAnimation(fromXScale,toXScale,fromYScale,toYScale,pivotXType,pivotX,pivotYType,pivotY)
animation.setDuration(1000);
这里的
fromXScale
和toXScale
分别确定了开始和结尾的缩放比例,而pivotX
确定了缩放的中心
效果如下:
[查看图片]
位移动画(Translate)
xml方法代码
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0%"
android:toYDelta="130%"
/>
对应Java方法代码
animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0,1,0,1,0,1,1.30f);
//TranslateAnimation(fromXType,fromXDelta,toXType,toXDelta,fromYType,fromYDelta,toYType,toYDelta)
//这里为了控制代码长度,将Animation.RELATIVE_TO_SELF换成其对应值1
//还有一种常用的动画 TranslateAnimation(fromXDelta,toXDelta,fromYDelta,toYDelta)
//这时Delta代表的是坐标值
animation.setDuration(1000);
位移动画(Translate)的参数与旋转动画(Rotate)中的使用方法一致,不同的fromXType有不同的fromXDelta解释与之对应。
但要注意,Translate的百分数位置起始点是其左上角,即当fromDelta为0时,图像从左上角出发
效果如下:
[查看图片]
复合动画(Set)
顾名思义,Set动画即前几个动画的复合使用
xml方法代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator"
android:shareInterpolator="true"
android:duration="2000"
>
<alpha
android:fromAlpha="0.5"
android:toAlpha="1.0"
/>
<scale
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0"
/>
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0%"
android:toYDelta="100%"
/>
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:pivotY="50%"
android:pivotX="50%"
android:repeatCount="100"
/>
</set>
对应Java方法代码
AnimationSet animationSet = new AnimationSet(true);
//AnimationSet(shareInterpolator)
animationSet.addAnimation(animation);
//只需将之前的其他动画的animation “加入” animationSet即可
binding.tween.startAnimation(animationSet);
AnimationSet(shareInterpolator)
中的shareInterpolator是一个boolean类型的值其为true时代表所有子动画共用一个Interpolator , linear_interpolator代表动画变化均匀
效果如下:
[查看图片]
Tween Animation的监听
动画监听器可以对动画的执行状态进行监听,并通过重写进行一系列操作
Animation.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animation animation) {
//监听动画开始
//对应animation.start()
}
@Override
public void onAnimationRepeat(Animation animation) {
//监听动画重复
}
@Override
public void onAnimationCancel()(Animation animation) {
//监听动画被取消
//对应animation.cancel()
}
@Override
public void onAnimationEnd(Animation animation) {
//监听动画执行结束
}
});
补间动画的优缺点
优点:使用简单,效果流畅,相比于逐帧动画来说不用准备大量素材
缺点:作用于视图对象View,未作用于属性。即所有View的移动、隐藏、旋转仅仅是看到的动画效果,实际View的位置/大小/比例并没有发生本质上的改变。