Android进阶(7)| 动画
一.View动画
1.View动画的种类
Android中的View动画分为平移动画、缩放动画、旋转动画、透明度动画和帧动画。其中帧动画的表现形式和前四种的变换效果不太一样,所以在后面会单独来介绍,这里先来介绍前4种。
2.View动画详解
动画的子类:View动画的前四种变换效果对应着Animation的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation和AlphaAnimation。
创建的方法:前4种动画既可以通过XML定义,创建的位置是res/anim/filename,也可以通过代码来进行动态的创建,不过对于View动画来说,还是建议采用XML来进行定义动画。要使用View动画,首先要创建出动画的XML文件,下面给出每一个标签里面的具体属性:
-
<set>
<set>标签表示动画的集合,对应着AnimationSet类,它可以包含若干个动画并且在它的内部也可以嵌套其他动画的集合,它的两个属性的含义如下:
1) android:interpolator:动画集合所采用的插值器,插值器会影响动画的速度。该属性可以不指定,默认为@android:mnim/acclerate_decelerate_interpolator,即加速/减速插值器。
2) android:sharedInterpolator:表示集合中的动画是否和集合共享一个插值器,如果集合不指定插值器,那么子动画就要单独指定所需的插值器或者是使用默认值。 -
<translate>
该标签表示的是平移动画,对应着TranslateAnimation类,它可以使一个View在水平和竖直方向完成平移的动画效果,它的一系列属性的含义如下:
1) android:fromXdelta:表示x的起始值,比如0;
2) android:toXDelta:表示x的结束值,比如100;
3) android:fromYDelta:表示y的起始值;
4) android:toYDelta:表示y的结束值; -
<scale>
该标签表示缩放动画,对应ScaleAnimation,它可以使View具有放大或者缩小的动画效果,它的属性如下
1) android:fromXScale:水平方向缩放的起始值;
2) android:toXScale:水平方向缩放的结束值;
3) android:fromYScale:竖直方向缩放的起始值;
4) android:toYScale:竖直方向缩放的结束值;
5) android:pivotX:缩放轴点的x的坐标,轴点默认是view的中心;
6) android:pivotY:缩放轴点的y坐标; -
<rotate>
该标签表示的是旋转动画,对应于RotateAnimation,它可以使View具有旋转的的动画效果,它的属性如下:
1) android:fromDegrees:旋转开始的角度;
2) android:toDegrees:旋转结束的角度;
3) android:pivotX:旋转轴点的x的坐标;
4) android:pivotY:旋转轴点的y坐标; -
<alpha>
该标签表示透明度动画,对应着AlphaAnimation,它可以改变View的透明度,它的属性如下:
1) android:fromAlpha:表述透明度的起始值;
2) android:toAlpha:表示透明度的结束值; -
其他属性
除了上面介绍的标签内的属性,还有一些公共属性:
1) android:duration:动画的持续时间;
2) android:fliiAfter:动画结束后View是否停留在结束位置,true表示View停留,false表示不停留;
实际操作的例子:首先我们要新建一个xml文件,如下所示:
// res/anim/animation_test.xml
...
<set xmlns:android="http://..."
android:fillAfter="true"
android:zAdjustment="normal">
<translate
android:duration="100"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="100"
android:toYDelta="100"
android:interpolator="@android:anim/linear_interpolator"/>
<rotate
android:duration="400"
android:fromDegrees="0"
android:toDegrees="90"/>
</set>
我们在定义了一个平移和缩放的动画集合,接着我们将这个动画设置到一个按钮上:
Button mButton = (Button)findViewById(R.id.button1);
Animation animation = AnimationUtils.loadAnimation(this,R.anim.animation_test); //给出具体的动画位置
mButton.startAnimation(animation); //启动动画
3.帧动画
概念:帧动画是按顺序播放一组预先定义好的图片,类似与电影播放。之所以说它与前四种View动画不同,是因为系统为它单独提供了另一个类AnimationDrawable。
创建方法:帧动画是需要使用XML来进行定义一个AnimationDrawable,实例如下:
// res/drawable/frame_animation.xml
...
<animation-list xmlns:android="http://..."
android:onshot="false">
<item android:drawable="图片1"/>
<item android:drawable="图片2"/>
<item android:drawable="图片3"/>
/>
如果要将上述的Drawable设置到View上,则需要进行如下操作:
Button mButton = (Button)findViewById(R.id.button1);
mButton.setBackgroundResource(R.drawable.frame_animation); //先将Drawable设置为背景
AnimationDrawable drawable = (AnimationDrawable) mButton.getBackground();
drawable.start();
二.View动画的特殊使用场景
1.LayoutAnimation
概念:LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时都会有这种效果。这种效果常常被用在ListView上,即它每一个item都会以一定的动画形式出现。
使用方法:首先还是需要在XML中定义:
// res/anim/anim_layout.xml
<LayoutAnimation xmlns:android="http://..."
android:delay="0.5"
android:animationOrder="normal"
android:animation="@anim/anim_item"
/>
它的各个属性的介绍如下:
-
android:delay
表示子元素开始动画的时间延迟,例如子元素入场动画的时间周期时300ms,那么0.5就表示每个子元素都需要延迟150ms才能播放入场动画。 -
android:animationOrder
表示子元素动画的顺序,有三种选项:normal(顺序显示)、reverse(逆向显示)和random(随机显示)。 -
Android:animation
为子元素指定具体的入场动画。
在XML中定义好了之后,就需要为子元素设置具体的入场动画:
// res/anim/anim_item.xml
<set xmlns:android="http://..."
android:duration="300"
android:interpolator="@android:anim/accelerate_interpolator"
android:sharedInterpolator="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<translate
android:fromXDelta="500"
android:toXDelta="0" />
</set>
最后在ViewGroup中指定android:LayoutAnimation属性即可:
<ListView
...
android:LayoutAnimation="@anim/anim_layout.xml"
...
/>
2.Activity的切换效果
使用方法:Activity的切换我们也可以使用自定义的效果,这里时主要用到overridePending Transition(int enterAnim,int exitAnim)这个方法,其中enterAnim表示的是Activity被打开时的动画资源id,exitAnim时Activity被暂停时的动画资源id。具体的使用方法如下:
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
public void finish(){
super.finish();
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}
注意:overridePending Transition(int enterAnim,int exitAnim)方法必须要在startActivity()或者是finish()之后被调用才能生效。
三.属性动画
属性动画时API11新加入的特性,和View动画所不同的是,它对作用对象进行了拓展,属性动画可以对任何对象(甚至不是对象的)做动画,并且不再拘泥于View动画的5种模式。
1.使用属性动画
实现原理:属性动画是在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。在属性动画中几个比较常用的动画类是:ValueAnimator、ObjectAnimator和AnimmatorSet,其中ObjectAnimator继承自ValueAnimator,而AnimmatorSet则表示一个动画的集合。
操作方法:举例来说:
(1)让一个对象沿着y轴向上平移一段距离:
ObjectAnimator.ofFloat(myObject,"translationY",-myObject.getHeight()).start();
(2)改变一个对象的背景色属性:
ValueAnimator colorAnim = ObjectAnimator.ofInt(this,"backgroundColor",0xfff8080,0xff8080ff); //实现从0xfff8080到0xff8080ff的颜色渐变
colorAnim.setDuration(3000); //设置时间
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE); //设置循环的次数
colorAnim.setRepeatMode(ValueAnimator.REVERSE); //动画的模式
colorAnim.start();
除了可以使用代码来动态的创建动画外,也可以使用XML来静态的创建动画,属性动画需要定义再res/amimator目录下,其中<set>标签对应着AnimatorSet,<animator>对应着valueAnimator,而<objectAnimator>则对应着ObjectAnimator。举例来说:
// res/animator/animator.xml
<set android:ordering="together" 设置动画组的播放顺序,这里是顺序播放
<objectAnimator
android:propertyName="x" 改变属性的名称
android:duration="300" 持续时间
android:valueTo="200" 属性改变之后的值
android:valueType="intType"/> 指定属性类型为整型
<objectAnimator
android:propertyName="y"
android:duration="300"
android:valueTo="300"
android:valueType="intType"/>
</set>
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,R.anim.animator); //设置动画
set.setTarget(mButton); //设置控件
set.start();
2.插值器和估值器
插值器的概念:即TimeInterpolator,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比。系统预置的插值器有:LinearInterpolator(匀速)、AcclerateDecelerateInterpolat-or(两头慢中间快)和DecelerateInterpolator(越来越慢)。
估值器的概念:即TypeEvaluator,它的作用是根据当前属性改变的百分比来计算改变后的属性。系统预置的估值器有IntEvaluator(整型)、FloatEvaluator(浮点型)和ArgeEvaluator(Color属性)。
工作过程:
4.属性动画的监听器
监听器的种类:属性动画的监听器主要用于监听动画的播放过程,主要是有如下的两个接口:AnimatorUpdateListener和AnimatorListener。
监听器的定义:
(1)AnimationListener
public static interface AnimatorListener{
void onAnimationStart(Animator animation); //动画开始
void onAnimationEnd(Animator animation); //动画结束
void onAnimationCancel(Animator animation); //动画取消
void onAnimationRepeat(Animator animation); //动画重复
}
(2)AnimatorUpdateListener
public static interface AnimatorUpdateListener{
void onAnimationUpdate(ValueAnimator animation); //当动画的某一帧出现改变时就会被调用
}
5.对任意属性做动画
属性动画的原理:属性动画的要求时被动画作用的对象能够提供该属性的get和set方法,属性动画就根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,直到所传递的值达到于最终值。
实现属性动画的条件:基于上述的原理,我们时能够对某一对象(object)的任意属性(abc)设置属性动画的,不过如果想让动画生效,还是需要同时满足以下两个条件的:
1)object必须提供setAbc()方法,如果动画的时候没有提供初始值,则还需要提供getAbc()方法来获取到该属性的初始值。
2)object的setAbc()对属性abc所做的改变必须能够通过某种形式反映出来,否则动画无效。
对于无法满足条件的对象解决方案:从上面可以看出我们如果要对某一对象实现属性动画,就必须要满足它所要求的两个条件,但是在很多情况下我们的对象是无法满足这些条件的,针对这些情况,Android官方提供了3种解决方法:
-
给你的对象加上get()和set()方法
这种方式是最简单粗暴的,不过在绝大多数情况下我们都会因为没有足够的权限而无法给某一对象加上get()和set()方法,因为这些对象大都是在Android SDK内部自定义好的。 -
用一个类来包装原始对象,间接为其提供get()和set()方法
这种方法也比较好理解,举例来说:
public void performAnimate(){
ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();
}
@Override
public void onClick(View view){
if(v == mButton){
performAnimate();
}
}
private static class ViewWrapper{ //用类来封装get和set方法
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();
}
}
- 采用ValueAnimator,监听动画过程,自己实现属性的改变
ValueAnimator本身不作用于任何对象,但是它可以对一个值做动画,然后我们就可以监听其动画过程,在动画过程中修改我们的对象属性值,举例来说:
private void performAnimate(final View target,final int start,final int end){
ValueAnimator valueAnimator = new ValueAnimator.ofInt(1,100); //设置时间
ValueAnimator.addUpdateListener(new AnimatorUpdateListener(){
private IntEvaluator mEvaluator = new IntEvaluator(); //设置一个估值器
@Override
public void onAnimationUpdate(ValueAnimator animation){
int currentValue = (Integer) animator.getAnimatedValue(); //获得当前动画的进度
float fraction = animator.getAnimatedFraction(); //获得当前进度占整个动画的过程的比例
target.getLayoutParams.width() = mEvaluator.evaluate(fraction,start,end); //使用估值器进行计算
target.requestLayout();
}
});
valueAnimator.setDuration(5000).start();
}
@Override
public void onClick(View v){
if(v == mButton){
performAnimate(mButton,mButton.getWidth(),500);
}
}
四.使用动画的注意事项
-
OOM问题
当图片数量较多且图片较大时就很容易出现OOM,这种情况大多发生在帧动画当中。 -
内存泄漏
在属性动画中有一类无限循环的动画,在使用这些动画时我们需要记住在Activity退出时必须要对动画进行停止,否则将导致Activity无法释放从而造成内存泄漏(View动画不存在此问题)。 -
兼容性问题
动画在3.0一下的系统中存在兼容性问题,需要注意。 -
View动画的问题
View动画是对View的影像做动画,并不是真正改变View的状态,所以有的时候会出现View动画完成之后无法隐藏的现象,这个时候只需要调用view.clearAnimation()清除掉View动画即可。 -
不要使用px
在进行动画的是由尽量使用dp,px会在不同的设备上有不同的效果。 -
动画元素的交互
从Android3.0之后,属性动画的单击事件触发位置为移动后的位置,但是View动画仍然是停留在原位置。 -
硬件加速
在使用动画的过程中,建议采用硬件加速。