7.1 View 动画
View 动画对应这 Animation 的四个子类:
名称 | 标签 | 子类 | 效果 |
---|---|---|---|
平移动画 | <translate> | TranslateAnimation | 移动 View |
缩放动画 | <scale> | ScaleAnimation | 放大或缩小 View |
旋转动画 | <rotate> | RotateAnimation | 旋转 View |
透明度动画 | <alpha> | AlphaAnimation | 改变 View 的透明度 |
<set> 标签表示动画集合,对应 AnimationSet 类,它可以包含若干个动画,并且内部也可以嵌套动画集合。
android:interpolator
表示动画集合采用的插值器,影响动画的速度,可以不指定为匀速。android:shareInterpolator
表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么自动化就需要单独指定所需的插值器或者使用默认值。
<translate>标签表示平移动画,对呀 TranslateAnimation 类,它可以使一个 View 在水平和竖直方向完成平移动画效果。
android:fromXDelta:表示 x 的起始值,比如 0;
android:toXDelta:表示 x 的结束值,比如 100;
android:fromYDelta:表示 y 的起始值;
android:toYDelta:表示 y 的结束值;
<scale>标签表示缩放动画,对应 ScaleAnimation,它可以使 View 具有放大或者缩小的动画效果。
android:fromXScale:水平方向的起始值,比如 0.5;
android:toXScale:水平方向缩放的结束值,比如 1.2;
android:pivotX:缩放轴点的x坐标,它会影响缩放的效果;
android:pivotY:缩放轴点的y坐标,它会影响缩放的效果;
android:toXScale:水平方向缩放的结束值,比如1.2;
android:toYScale:竖直方向缩放的起始值;
<rotate>标签表示旋转动画,对应于 RotateAnimation,它可以使 View 具有旋转效果。
android:fromDegrees:旋转开始的角度,比如 0;
android:toDegrees:旋转结束的角度,比如180;
android:pivotX:旋转轴点的 x;
android:pivotY:旋转轴点的 y;
<alpha>表示透明动画。对应的 AlphaAnimation。
android:fromAlpha:表示透明度的起始值,比如0.1
android:toAlpha:表示透明度的结束值,比如1
上面都只是很简单的介绍了XM格式,具体的使用方法还是看文档,我们还有一些常用的属性如下
android:duration:动画的时间
android:fillAfter:动画结束之后是否停留在结束的位置
演示一个一秒钟平移 100 加旋转 360 度然后复原的案例:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="false">
<translate
android:fromYDelta="0"
android:fromXDelta="0"
android:toXDelta="100"
android:toYDelta="100"
android:duration ="1000"
android:interpolator = "@android:anim/linear_interpolator"
/>
<rotate
android:duration ="1000"
android:fromDegrees="0"
android:toDegrees="360"
/>
</set>
再加一个代码写一个 透明动画:
Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.my_anim);
button.startAnimation(animation);
//代码动画
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(1000);
animation.setAnimationListener(new Animation.AnimationListener() {
//开始
@Override
public void onAnimationStart(Animation animation) {}
//结束
@Override
public void onAnimationEnd(Animation animation) {}
//重复
@Override
public void onAnimationRepeat(Animation animation) { }
});
button2.startAnimation(alphaAnimation);
7.1.2 自定义 View 动画
自定义动画只需要继承 Animation,然后重写它的 initialize 和 applyTransformation 方法
- initialize:初始化
-
applyTransformation:进行相应的矩阵变换,很多时候需要采用 Camera 来简化矩阵变换的过程。
可以参照 ApiDemos 中的 Rotate3dAnimation。
7.1.3 帧动画
帧动画使用很简单,但是比较容易引起 OOM,所以要避免使用大尺寸图片。
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@mipmap/timg" android:duration="500"/>
<item android:drawable="@mipmap/timgb" android:duration="500"/>
<item android:drawable="@mipmap/timgc" android:duration="500"/>
</animation-list>
button.setBackgroundResource(R.drawable.my_list);
AnimationDrawable background = (AnimationDrawable) button.getBackground();
background.start();
7.2 View 动画的特殊使用场景
7.2.1 LayoutAnimation
LayoutAnimation 作用于 ViewGroup,为 ViewGroup 指定一个动画,这样当它的子元素出场时都会具有这种动画效果。
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
android:animationOrder="normal"
android:animation="@anim/anim_item">
</layoutAnimation>
android:delay
表示子元素开始动画的延迟,0.5 就是一半的时间周期。android:animationOrder
表示子元素动画的顺序。normal:顺序显示
reverse:逆向显示
random:随机显示
android:animation
为子元素指定具体的入场动画。
先新建一个 set 动画集合,里面是从透明到不透明并且平移的一个效果
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:interpolator="@android:anim/accelerate_interpolator"
android:shareInterpolator="true" >
<alpha android:fromAlpha="0.0" android:toAlpha="1.0" />
<translate android:fromXDelta="500" android:toXDelta="0" />
</set>
新建一个 layoutAnimation ,设置显示样式效果
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/anim_item"
android:animationOrder="normal"
android:delay="0.5">
</layoutAnimation>
在 XML 中指定 LayoutAnimation
<ListView
android:id="@+id/list_view"
android:layoutAnimation="@anim/my_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
或者可以通过 LayoutAnimationConreoller 来实现
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
mListView.setLayoutAnimation(controller);
7.2.2 Activity 的切换效果
Activity 的自定义切换效果。主要用到 overridePendingTransition (int enterAnim,int enterAnim)这个方法。
- enterAnim: Activity 被打开时,所需要的动画资源 id;
- exitAnim: Activity 被暂停时,所需要的动画资源 id;
Intent intent = new Intent(MainActivity.this, ListViewActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.my_anim,R.anim.my_anim);
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.my_anim,R.anim.my_anim);
}
7.3 属性动画
7.3.1 使用属性动画
属性动画可以对任意对象进行动画而不仅仅是 View,几乎是无所不能的,只要有对象这个属性。
改变一个对象的 translationX 值。
ObjectAnimator.ofFloat(mButton, "translationX",200).start();
改变一个对象的背景颜色属性。
ObjectAnimator colorAnim = ObjectAnimator.ofInt(mButton, "backgroundColor", 0xFFFF8080, 0xFF8080FF);
colorAnim.setDuration(2000);
colorAnim.setEvaluator(new ArgbEvaluator());
//INFINITE 表示无限次
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
//REVERSE 表示反转效果
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();
动画集合,5秒内对 View 的旋转、平移、缩放和透明度都进行了改变
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(mButton,"rotationX",0,360),
ObjectAnimator.ofFloat(mButton,"rotationY",0,360),
ObjectAnimator.ofFloat(mButton,"rotation",0,-180),
ObjectAnimator.ofFloat(mButton,"translationX",0,90),
ObjectAnimator.ofFloat(mButton,"translationY",0,90),
ObjectAnimator.ofFloat(mButton,"scaleX",1,1.5f),
ObjectAnimator.ofFloat(mButton,"scaleY",0,2.5f),
ObjectAnimator.ofFloat(mButton,"alpha",1,0.25f,1)
);
set.start();
属性动画除了通过代码实现以外,还可以通过 XML 来定义,定义在 res/animator/ 目录下。
<objectAnimator
android:duration="3000"
android:propertyName="x"
android:valueFrom="-50"
android:valueTo="200"
android:valueType="floatType" />
<objectAnimator
android:propertyName="y"
android:duration="3000"
android:valueFrom="1"
android:valueTo="300"
android:valueType="floatType" />
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(ValueActivity.this, R.animator.my_animator);
set.setTarget(mButton);
set.start();
android:propertyName:表示属性动画作用对象的属性的名称
android:duration:表示动画的时长
android:valueFrom:表示属性的起始值
android:valueTo:表示属性的结束值
android:startOffset:表示动画的延迟时间,当动画开始后,需要延迟多少毫秒才会真正的播放
android:repeatCount:表示动画的重复次数,默认 0,-1 表示无线循环
android:repeatMode:表示动画的重复模式
restart:表示连续重复
reverse:表示连逆向重复,就是反转
android:valueType:表示 propertyName 有两个属性有 int 和 float 两个可选项,分别表示属性的类型,和浮点型,另外,如果所制定的是颜色类型,那么就不需要指定 propertyName,系统会自动对颜色类型进行处理
实际开发建议采用代码来实现,简便动态。
7.3.2 理解插值器和估值器
TimeInterpolator:中文翻译是时间插值器的意思,他的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有:
LinearInterpolator:(线性加速器,匀速加速器),加速和减速插值器
AccelerateDecrateInterpolator:(加速减速插值器:动画两头慢中间快)
DecelerateInterpolator:(减速插值器:动画越来越慢)
TypeEvaluator:的中文翻译是类型估值算法,也叫估值器,他的作用是根据当前属性变化的百分比来计算变化后的属性值,系统也预设的有:
- IntEvaluator:整型
- FloatEvaluator:浮点型
- ArgbEvaluator:Color属性
属性动画中的插值器和估值器很重要,他们实现非匀速动画的重要手段。也可以自定义使用。
public class LinearInterpolator implements Interpolator{
@Override
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, 2);
}
}
public class MyEvaluator extends IntEvaluator {
//fraction 百分比、startValue 开始值、endValue 结束值
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
return super.evaluate(fraction, startValue, endValue);
}
}
7.3.3 属性动画的监听器
属性动画提供监听器用于监听动画的播放,主要有两个接口:
- AnimatorListener
public static interface AnimatorListener {
//开始
void onAnimationStart(Animator animation);
//结束
void onAnimationEnd(Animator animation);
//取消
void onAnimationCancel(Animator animation);
//重复
void onAnimationRepeat(Animator animation);
}
- **AnimatorUpdateListener **
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
AnimatorUpdateListener 比较特殊,它会监听整个动画过程的,每播放一帧,就会调用一次 AnimatorUpdateListener。
7.3.4 对任意属性做动画
给 Button 加一个动画,增加 Button 的宽度到 500 px。View 动画只能通过 缩放 Scale 来让 Button 在 x 方向上被放大,而且有可能超过屏幕大小,而属性动画可以并且不会超越屏幕。
方法 1:直接使用(TextView 和其子类)
ObjectAnimator.ofInt(mButton, "width",50000).setDuration(2000).start();
方法 2:用一个类包装原始对象,间接为其提供 get 和 set 方法。
private void performAnimate() {
ViewWrapper viewWrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(viewWrapper, "width",500).setDuration(2000).start();
}
public static class ViewWrapper {
private View mTarget;
public ViewWrapper(View target) {
mTarget = target;
}
public int getWidth() {
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
ViewWrapper viewWrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(viewWrapper, "width",500).setDuration(2000).start();
方法 3:采用 ValueAnimator,监听动画过程的每一帧,自己实现属性的改变
private void performAnimator(final View target, final int start, final int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//持有一个IntEvaluator对象,方便下面估值的时候使用
private IntEvaluator mEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获得当前动画的进度值,整形1-100之间
int currentValue = (int) animation.getAnimatedValue();
//获得当前进度占整个动画之间的比例,浮点0-1之间
float fraction = animation.getAnimatedFraction();
//直接使用整形估值器,通过比例计算宽度,然后再设置给 Button
target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
target.requestLayout();
}
});
valueAnimator.setDuration(5000).start();
}
performAnimate(mButton,mButton.getWidth(),5000);
方法 4:给你的对象加上 get 和 set,如果你有权限的话(一般没有,所以可以不用)
7.3.5 属性动画的工作原理
属性动画要求动画作用的对象提供该属性的 set 方法,属性动画根据那你传递的该属性的初始值和最终值,以动画的效果多次去调用 set 方法,每次传递给 set 方法的值都不一样,确切的来说随着时间的推移,所传递的值越来越接近最终值,如果动画的时候没有传递初始值,那么还要提供 get 方法,因为系统需要获取属性的初始值。这就上面的 方法 2 需要设置 get 和 set 的原因。
一句话:最后是通过反射来调用。(源码不清不楚,先放着)
7.4 使用动画的注意事项
OOM 问题
这个问题主要出现在帧动画,当图片较多且大就容易出现,这个时候尽量避免使用帧动画。内存泄漏
在属性动画中有设置无线循环的动画,这类动画要在退出时要及时停止,View 动画并不在此问题。** 兼容性问题**
动画在 3.0 以下的系统上有兼容问题,要做好适配工作。View 动画的问题
View 动画是对 View 的影像做动画,并不是正真的改变 View 的状态,调用方法不能正常使用时,调用 View.clearAnimation 清除可以解决。不要使用 px
在进行动画的过程中,要使用 dp,用 px 会有适配问题。动画元素的交互
3.0 之前的问题忽略,3.0 以后属性动画的点击事件触发位置为移动后的位置,但是 View 动画任然在原位置。硬件加速
使用动画的过程中,建议开启硬件加速,这样会提供动画的流畅性。