自定义view————Animation实践篇

自定义view————Animation实践篇

深深的想扎根于自定义view之中,做出酷炫的效果,酷炫的效果很多时候都是借助动画来的,今天介绍一下在Android中开发动画效果的两套框架,分别为 Animation 和 Property Animation;

相对来说,Property Animation比Animation要强大太多,两者之间的主要区别在于:

区别一:需要的Anroid API level不一样
Property Animation需要android API level 11的支持,当然可以使用nineoldandroids.jar进行兼容,而View Animation则是最原始的版本。

区别二:适用范围不一样
Property Animation适用于所有的Object对象,而View Animation则只能应用于View对象。

区别三,实际改变不一样:
Animation:改变的是view的绘制位置,而非实际属性;
Property Animation:改变的是view的实际属性;

如:用两套框架分别对一个button做位移动画,用animation处理之后的点击响应会在原处,而用Property Animation处理后的点击响应会在最终位置处;

Animation

Animation又分为Tween Animation(补间动画) 和Frame Animation(帧动画)

帧动画(Frame Animation):

帧动画其实就是按照一定的时间间隔进行快速的图片切换,达到视觉上的动画效果,这种动画相对来说缺点较多,比如:

1.根据动画的复杂度需要切多张图,这样就不可避免的增大了包大小

2.由于是多张图,运行时需要加载到内存,那么又会增大运行时的内存大小

所以如果在动效上有别的方案选择,个人不太建议使用帧动画的实现方式,当然有时候不得不用,这时我们可以尽可能的在效果连贯的基础上减少图片张数;
帧动画的实现也相对简单,就举一个例子,但是例子之中有几条帧动画的使用规则,是我以前在使用的过程中遇到问题后总结出来的;

1.在drawable目录下xml中定义如下文件,节点为animation-list,oneshot代表是否执行一次,duration代表每张图对应展示时间:

1
2
3
4
5
6
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
<item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
<item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>

2.然后将该drawable设置给对应的imageview

3.在activity中

animDrawable = (AnimationDrawable) imageView.getBackground();

4.在onWindowFocusChanged()中调用:

animDrawable.stop();

animDrawable.start();

补间动画(Tween Animation):

Animation下有五个子类:AlphaAnimation(渐变),RotateAnimation(旋转),ScaleAnimation(缩放),TranslateAnimation(位移)

1)xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<scale>
<!-- 单次运行时间 -->
android:duration="500"
<!-- 运行完成后是否保持结束时的状态 -->
android:fillAfter="true"
<!-- 运行完成后是否回到开始时的状态 -->
android:fillBefore="false"
<!-- 初始时大小,1代表原大小,0代表无 -->
android:fromXScale="1"
android:fromYScale="1"
<!-- 使用的插值器,控制运行过程中的速率 -->
android:interpolator="@android:anim/accelerate_interpolator"
<!-- 相对中心点,50%代表自身中心,50%p代表相对父view的中心 -->
android:pivotX="50%"
android:pivotY="50%"
<!-- 重复的次数,infinite代表永久循环 -->
android:repeatCount="infinite"
<!-- 重复的模式, restart代表重新开始,reverse代表反转-->
android:repeatMode="restart"
<!-- 延迟多久后开始 -->
android:startOffset="100"
<!-- 要到达的缩放比例 -->
android:toXScale="0"
android:toYScale="0" />
</scale>
<translate
android:duration="550"
<!-- 相对当前位置的像素距离 -->
android:fromYDelta="300"
android:interpolator="@android:anim/accelerate_interpolator"
android:toYDelta="0" />
<alpha
android:duration="550"
android:fromAlpha="0"
android:toAlpha="1" />
</set>

代码中再去调用

1
2
3
Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.animation1);
view.startAnimation(animation);

2)java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// false代表里面的子animation不共用一个插值器
AnimationSet animationSet = new AnimationSet(false);
// 从alpha 完全透明变为完全不透明
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(500);
alphaAnimation.setFillAfter(true);
// 运行完成后是否回到开始时的状态
alphaAnimation.setFillBefore(false);
alphaAnimation.setInterpolator(new AccelerateInterpolator());
// 重复的次数,infinite代表永久循环
alphaAnimation.setRepeatCount(Animation.INFINITE);
// 重复的模式, restart代表重新开始,reverse代表反转
alphaAnimation.setRepeatMode(Animation.RESTART);
// 给动画设置对应的监听,可以在动画开始、结束或重复执行时做对应操作
alphaAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//开始时候回调
}
@Override
public void onAnimationRepeat(Animation animation) {
//重复执行时回调
}
@Override
public void onAnimationEnd(Animation animation) {
//一轮结束时回调
}
});
RotateAnimation rotateAnimation = new RotateAnimation(0, 360);
rotateAnimation.setDuration(500);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(rotateAnimation);
view.startAnimation(animationSet);

Property Animation(属性动画)

在Property Animation中,可以对动画应用以下属性:

1.TimeInterpolator(插值器,和低版本的Interpolator一样):属性值的计算方式,如先快后慢

2.TypeEvaluator:根据属性的开始、结束值与TimeInterpolator计算出的因子计算出当前时间的属性值

3.Repeat Count and behavoir:重复次数与方式,如播放3次、5次、无限循环,可以此动画一直重复,或播放完时再反向播放

4.Animation sets:动画集合,即可以同时对一个对象应用几个动画,这些动画可以同时播放也可以对不同动画设置不同开始偏移

5.Frame refreash delay:多少时间刷新一次,即每隔多少时间计算一次属性值,默认为10ms,最终刷新时间还受系统进程调度与硬件的影响

ValueAnimator

ValueAnimator包含了 Property Animation 动画的所有核心功能,如动画时间,开始、结束属性值,相应时间属性值计算方法等。

例:ValueAnimator 在2S内将view横向拉长为2倍,纵向压缩为0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 在2S内将view横向拉长为2倍,纵向压缩为0
// 创建0-1的一个过程,任何复杂的过程都可以采用归一化,然后在addUpdateListener回调里去做自己想要的变化
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
// 设置过程的时间为2S
valueAnimator.setDuration(SCALE_ANIM_TIME);
// 设置这个过程是速度不断变快的
valueAnimator.setInterpolator(new AccelerateInterpolator());
// 这个过程中不断执行的回调
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 不断回调的在0-1这个范围内,经过插值器插值之后的返回值
float value = (Float) animation.getAnimatedValue();
// ViewHelper可直接用于修改view属性
// 将宽在2S内放大一倍
ViewHelper.setScaleX(mTestImage, 1 + value);
// 将高在2S内压缩为0
ViewHelper.setScaleY(mTestImage, 1 - value);
}
});
// AnimatorListenerAdapter是AnimatorListener的空实现,根据需要覆盖的方法自行选择
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
Toast.makeText(getApplicationContext(), "onAnimationStart", Toast.LENGTH_SHORT)
.show();
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
Toast.makeText(getApplicationContext(), "onAnimationEnd", Toast.LENGTH_SHORT)
.show();
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
}
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
}
});
valueAnimator.start();

android插值器有如下效果:

AccelerateInterpolator      加速,开始时慢中间加速
DecelerateInterpolator       减速,开始时快然后减速
AccelerateDecelerateInterolator  先加速后减速,开始结束时慢,中间加速
AnticipateInterpolator       反向 ,先向相反方向改变一段再加速播放
AnticipateOvershootInterpolator  反向加回弹,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值
BounceInterpolator        跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
CycleInterpolator         循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 mCycles Math.PI * input)
LinearInterpolator         线性,线性均匀改变
OvershootInterpolator       回弹,最后超出目的值然后缓慢改变到目的值
TimeInterpolator         一个接口,允许你自定义interpolator,以上几个都是实现了这个接口

其实想实现对应的效果,其实是找一条曲线对对应条件进行模拟,然后根据曲线函数,和X值,得出每个时间点上对应的Y值(插值),这也就是插值器原理;

ObjectAnimator

再次实现这个例子:ValueAnimator 在2S内将view横向拉长为2倍,纵向压缩为0:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
AnimatorSet animatorSet = new AnimatorSet();
// 将view在x方向上从原大小放大2倍
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(mTestImage, "scaleX", 1, 2);
scaleXAnimator.setDuration(SCALE_ANIM_TIME);
// 将view在y方向上从原大小压缩为0
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(mTestImage, "scaleY", 1, 0);
scaleYAnimator.setDuration(SCALE_ANIM_TIME);
// 设置加速模式
animatorSet.setInterpolator(new AccelerateInterpolator());
// 设置回调,当然也可以设置在单独的animator上,eg:scaleXAnimator
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
Toast.makeText(getApplicationContext(), "onAnimationStart", Toast.LENGTH_SHORT)
.show();
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
Toast.makeText(getApplicationContext(), "onAnimationEnd", Toast.LENGTH_SHORT)
.show();
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
}
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
}
});
animatorSet.playTogether(scaleXAnimator, scaleYAnimator);
animatorSet.start();

ObjectAnimator 是ValueAnimator 的子类,可以直接改变Object的属性,目前可供改变的属性主要有:
translationX,translationY View相对于原始位置的偏移量
rotation,rotationX,rotationY 旋转,rotation用于2D旋转角度,3D中用到后两个
scaleX,scaleY 缩放比
x,y View的最终坐标,是View的left,top位置加上translationX,translationY
alpha 透明度

animation讲的差不多了,接下来实现一个仿QQ悬浮窗动效

仿QQ悬浮窗动效 github demo

效果图

坚持原创技术分享,您的支持将鼓励我继续创作!
Fork me on GitHub