概述
衡量一个产品的价值,首先就考虑它的用户体验,无非就是它的实用性,高效性和易上手。当然更重要的是产品呈现的内容,这部分是属于运营的,在这里不赘述。其实还有一个很重要的点容易被忽略的,就是产品的UI。
它是首先呈现给用户的视觉体验,界面是否美观,布局是否合理,配色是否协调等等都是引流的关键,另外还有一个重要的点,就是产品中的动画。这也是本篇重点介绍的。
动画分类
Android中的动画主要分为三大类:
- View动画
- 帧动画
- 属性动画
View动画
定义: View动画也叫补间动画,包含的功能有:淡入淡出: alpha。位移:translate。缩放:scale。旋转: rotate
API:
translate(平移):
1
2
3
4
5
6
7
8
9
10
11Animation translateAnimation = new TranslateAnimation(0,500,0,500);
// 创建平移动画的对象:平移动画对应的Animation子类为TranslateAnimation
// 参数分别是:
// 1. fromXDelta :视图在水平方向x 移动的起始值
// 2. toXDelta :视图在水平方向x 移动的结束值
// 3. fromYDelta :视图在竖直方向y 移动的起始值
// 4. toYDelta:视图在竖直方向y 移动的结束值
translateAnimation.setDuration(3000);
// 播放动画直接 startAnimation(translateAnimation)
//如:
mButton.startAnimation(translateAnimation);scale(缩放):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Animation scaleAnimation= new ScaleAnimation(0,2,0,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
// 1. fromX :动画在水平方向X的结束缩放倍数
// 2. toX :动画在水平方向X的结束缩放倍数
// 3. fromY :动画开始前在竖直方向Y的起始缩放倍数
// 4. toY:动画在竖直方向Y的结束缩放倍数
// 5. pivotXType:缩放轴点的x坐标的模式
// 6. pivotXValue:缩放轴点x坐标的相对值
// 7. pivotYType:缩放轴点的y坐标的模式
// 8. pivotYValue:缩放轴点y坐标的相对值
// pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
// pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
// pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
scaleAnimation.setDuration(3000);
// 使用
mButton.startAnimation(scaleAnimation);rotate(旋转):
1
2
3
4
5
6
7
8
9
10
11
12Animation rotateAnimation = new RotateAnimation(0,270,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
// 1. fromDegrees :动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
// 2. toDegrees :动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
// 3. pivotXType:旋转轴点的x坐标的模式
// 4. pivotXValue:旋转轴点x坐标的相对值
// 5. pivotYType:旋转轴点的y坐标的模式
// 6. pivotYValue:旋转轴点y坐标的相对值
// pivotXType = Animation.ABSOLUTE:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
// pivotXType = Animation.RELATIVE_TO_SELF:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
// pivotXType = Animation.RELATIVE_TO_PARENT:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
rotateAnimation.setDuration(3000);
mButton.startAnimation(rotateAnimation);alpha(透明度):
1
2
3
4
5Animation alphaAnimation = new AlphaAnimation(1,0);
// 1. fromAlpha:动画开始时视图的透明度(取值范围: -1 ~ 1)
// 2. toAlpha:动画结束时视图的透明度(取值范围: -1 ~ 1)
alphaAnimation.setDuration(3000);
mButton.startAnimation(alphaAnimation);动画组合:将上面的动画结合在一起播放:
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// 组合动画设置
AnimationSet setAnimation = new AnimationSet(true);
// 特别说明以下情况
// 因为在下面的旋转动画设置了无限循环(RepeatCount = INFINITE)
// 所以动画不会结束,而是无限循环
// 所以组合动画的下面两行设置是无效的
setAnimation.setRepeatMode(Animation.RESTART);
setAnimation.setRepeatCount(1);// 设置了循环一次,但无效
// 旋转动画
Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
rotate.setDuration(1000);
rotate.setRepeatMode(Animation.RESTART);
rotate.setRepeatCount(Animation.INFINITE);
// 平移动画
Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,
TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
TranslateAnimation.RELATIVE_TO_SELF,0
,TranslateAnimation.RELATIVE_TO_SELF,0);
translate.setDuration(10000);
// 透明度动画
Animation alpha = new AlphaAnimation(1,0);
alpha.setDuration(3000);
alpha.setStartOffset(7000);
// 缩放动画
Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
scale1.setDuration(1000);
scale1.setStartOffset(4000);
// 将创建的子动画添加到组合动画里
setAnimation.addAnimation(alpha);
setAnimation.addAnimation(rotate);
setAnimation.addAnimation(translate);
setAnimation.addAnimation(scale1);
// 使用
mButton.startAnimation(setAnimation);动画监听:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21Animation.addListener(new AnimatorListener() {
public void onAnimationStart(Animation animation) {
//动画开始时执行
}
public void onAnimationRepeat(Animation animation) {
//动画重复时执行
}
public void onAnimationCancel()(Animation animation) {
//动画取消时执行
}
public void onAnimationEnd(Animation animation) {
//动画结束时执行
}
});
帧动画
原理:把一段视频剪成一帧一帧的图片,再把图片依次播放,与GIF动画是同样的原理。
使用步骤:
现在drawble下新建一个anination.xml文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@drawable/frame01" android:duration="100"/>
<item android:drawable="@drawable/frame02" android:duration="100"/>
<item android:drawable="@drawable/frame03" android:duration="100"/>
<item android:drawable="@drawable/frame04" android:duration="100"/>
<item android:drawable="@drawable/frame05" android:duration="100"/>
<item android:drawable="@drawable/frame06" android:duration="100"/>
<item android:drawable="@drawable/frame07" android:duration="100"/>
<item android:drawable="@drawable/frame08" android:duration="100"/>
<item android:drawable="@drawable/frame09" android:duration="100"/>
<item android:drawable="@drawable/frame10" android:duration="100"/>
<item android:drawable="@drawable/frame11" android:duration="100"/>
<item android:drawable="@drawable/frame12" android:duration="100"/>
<item android:drawable="@drawable/frame13" android:duration="100"/>
<item android:drawable="@drawable/frame14" android:duration="100"/>
<item android:drawable="@drawable/frame15" android:duration="100"/>
<item android:drawable="@drawable/frame16" android:duration="100"/>
<item android:drawable="@drawable/frame17" android:duration="100"/>
<item android:drawable="@drawable/frame18" android:duration="100"/>
</animation-list>android:oneshot控制该动画结束后是否循环播放,android:duration控制单帧的时间100毫秒
在布局中把上面的drawble当成背景设置进去,例如:
1
2
3
4
5
6
7
8<ImageView
android:layout_marginTop="50dp"
android:layout_centerHorizontal="true"
android:id="@+id/image"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@drawable/animation"
/>在aciticty中播放:
1
2
3
4
5ImageView image = findViewById(R.id.frame_image);
// 获取 AnimationDrawable 对象
AnimationDrawable animationDrawable = (AnimationDrawable) frame_image.getBackground();
animationDrawable.start();//播放
animationDrawable.stop();//停止播放帧动画十分容易引起OOM!
属性动画
属性动画常用的动画类:ValueAnimator、ObjectAnimator、AnimatorSet。ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合。
所有补间动画能实现的效果,属性动画都能实现。
平移
1
2//参数:目标对象,对应属性,更改值。
ObjectAnimator.ofFloat(myObject, "translationY", myObject.getHeight()).start();透明度
1
2
3
4
5
6
7
8
9
10
11//参数:目标对象,对应属性值,起始值,最终值
ValueAnimator anim = ObjectAnimator.ofInt(myObject, "backgroundColor",0xFFFF8080,0xFF8080FF);
//设置过度时间
anim.setDuration(3000);
//设置插值器
anim.setEvaluator(new ArgbEvaluator());
//重复次数
anim.setRepeatCount(ValueAnimator.INFINITE);
//播放模式,重新播放
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.start();动画集合播放
1
2
3
4
5
6
7
8
9
10
11
12AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(myView, "rotationX", 0, 360),
ObjectAnimator.ofFloat(myView, "rotationY", 0, 180),
ObjectAnimator.ofFloat(myView, "rotation", 0, -90),
ObjectAnimator.ofFloat(myView, "translationX", 0, 90),
ObjectAnimator.ofFloat(myView, "translationY", 0, 90),
ObjectAnimator.ofFloat(myView, "scaleX", 1, 1.5f),
ObjectAnimator.ofFloat(myView, "scaleY", 1, 0.5f),
ObjectAnimator.ofFloat(myView, "alpha", 1, 0.25f, 1)
);
set.setDuration(5*1000).start();动画对应的常用属性值:
在属性动画中,View的常用属性
alpha 透明度
rotation z轴旋转
rotationX x轴旋转
rotationY y轴旋转
translationX x水平偏移
translationY y水平偏移
ScaleX x轴缩放
ScaleY y轴缩放- 小结及原理说明
上面的目标对象可以是任何对象,不一定是iew对象,其中属性动画的原理是:
通过反射机制,以动画的效果多次调用set方法来改变属性值的。所以,使用属性动画时,相应的对象属性必须有set方法,get方法可以没有,但是如果使用动画的时候没有传递初始值,就必须提供get方法,因为系统要通过get方法获取属性的默认初始值。
所有属性动画是真正能改变目标对象的属性值的,与补间动画差别就在此。 插值器和估值器
插值器:用来修饰动画效果,定义动画的变化规率(变化趋势),比如平移动画,可以匀速平移也可以加速平移,这个由插值器决定。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public interface Interpolator {
// 内部只有一个方法
float getInterpolation(float input) {
// 参数说明
// input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化
// 即动画开始时,input值 = 0;动画结束时input = 1
// 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加
return xxx;
// 返回的值就是用于估值器继续计算的fraction值
}
}
Android按照运动的规律给我们提供常用的插值器:
- AccelerateInterpolator 加速
- DecelerateInterpolator 减速
- LinearInterpolator 匀速
- AccelerateDecelerateInterpolator 先加速再减速(在动画开始与结束的地方速率改变比较慢,在中间的时候最快)
- AnticipateInterpolator 开始的时候向后然后向前甩(先退后再加速前进)
- AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值(先退后再加速前进,超出终点后再回终点)
- BounceInterpolator 动画结束的时候弹起(最后阶段弹球效果)
- CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
- OvershootInterpolator 向前甩一定值后再回到原来位置(快速完成动画,超出再回到结束时的位置)使用:
1
2
3
4
5Animation alphaAnimation = new AlphaAnimation(1,0);
alphaAnimation.setDuration(3000);
Interpolator overshootInterpolator = new OvershootInterpolator();
alphaAnimation.setInterpolator(overshootInterpolator);
view.startAnimation(alphaAnimation);
估值器:用来决定具体的数值变化,比如(匀)加速平移时,“加速度”是多少由估值器决定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 public interface TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:插值器getInterpolation()的返回值
// startValue:动画的初始值
// endValue:动画的结束值
....// 估值器的计算逻辑
return xxx;
// 赋给动画属性的具体数值
// 使用反射机制改变属性变化
// 特别注意
// 插值器的input值 和 估值器fraction有什么关系呢?
// 答:input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation()
// 然后通过实现getInterpolation()中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction了
}
}
需要注意的是:插值器的input值 和 估值器fraction有什么关系呢?
input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation(),然后通过实现getInterpolation()中的逻辑算法,根据input值来计算出一个返回值,而这个返回值就是fraction了
系统自带的估值器有:
- IntEvaluator 以整型的形式从初始值到结束值 进行过渡
- FloatEvaluator 以浮点型的形式从初始值到结束值 进行过渡
- ArgbEvaluator 以Argb类型的形式从初始值到结束值 进行过渡