一.View动画的介绍
Android的动画分为两种:View动画(帧动画也属于View动画),属性动画。
View动画的作用对象是View,它支持4种动画效果:平移动画,缩放动画,旋转动画,透明度动画。帧动画也属于View动画,但帧动画的表现形式和上面的四种变换效果不太一样。
本文将介绍:
- View动画的四种类型以及动画集合set
- View动画的使用
- View动画的监听
- 自定义View动画
- 帧动画
- View动画的特殊使用场景
二.View动画的种类
View动画四种变换效果对应着Animation的四个子类:
- TranslateAnimation(平移动画,
) - ScaleAnimation(缩放动画,
) - RotateAnimation(旋转动画,
) - AlphaAnimation(透明度动画
)。
这四种动画既可以通过XML来定义,也可以通过代码来动态创建。对于View动画来说,建议采用XML来定义动画,因为XML格式的动画可读性更好。
View动画的XML格式的文件路径为:res/anim/**.xml。
三.View动画的四种类型的xml格式基本属性
所有value值都为float类型。
1.动画集合set
对应AnimationSet类,可以包含若干个动画,并且它的内部也是可以嵌套其他动画集合的
- android:interpolator:表示动画集合所采用的插值器,插值器影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。该属性可以不指定,默认为@android:anim/accelerate_decelerate_interpolator,即加速减速插值器。
- android:shareInterpolator:表示集合中的动画是否和集合共享同一个插值器。如果不指定插值器,那么子动画就需要单独指定所需的插值器或者使用默认值。
2.1 平移动画translate
对应TranslateAnimation类,View在水平和竖直方向的平移
- android:fromXDelt:表示x的起始值
- android:fromYDelt:表示y的起始值
- android:toXDelta:表示x的结束值
- android:toYDelta:表示y的结束值
2.2透明度动画alpha
对应AlphaAnimation类,改变View的透明度
- android:fromAlpha:表示透明度的起始值,比如0.1
- android:toAlpha:表示透明度的结束值,比如1
2.3缩放动画scale
对应ScaleAnimation类,使View具有放大和缩小的动画效果
- android:fromXScale:水平方向缩放的起始值
- android:fromYScale:竖直方向缩放的起始值
- android:pivotX:缩放的轴点x坐标,会影响缩放的效果
- android:pivotY:缩放的轴点y坐标,会影响缩放的效果
- android:toXScale:水平方向的结束值
- android:toYScale:竖直方向的结束值
2.4旋转动画rotate
对应RotateAnimation类,使View具有旋转的动画效果
- android:fromDegrees:旋转开始的角度
- android:pivotX:旋转的轴点x坐标
- android:pivotY:旋转的轴点y坐标
- android:toDegrees:旋转结束的角度
View动画还有一些常用的属性:
- android:duration:动画的持续时间;
- android:fillAfter:动画结束以后View是否停留在结束位置,true表示View停留在结束位置,false则不停留。
四.View 动画的使用
1.使用xml格式定义View动画
TextView tv = findViewById(R.id.tv);
Animation animation = AnimationUtils.loadAnimation(this,R.anim.animation_tv);
tv.startAnimation(animation);
2.使用通过代码创建的View 动画
/**这里写一个透明度动画,其他类型动画写法类似*/
AlphaAniamation alphaAnimation = new AlphaAnimation(0,1);//透明度由0到1
alphaAnimation.setDuration(300);//动画时间300ms
tv.startAnimation(alphaAnimation);
五.View 动画的监听
通过Animation的setAnimationListener给View动画添加过程监听。
public static interface AnimationListener {
void onAnimationStart(Animation animation);
void onAnimationEnd(Animation animation);
void onAnimationRepeat(Animation animation);
}
六.自定义View 动画
自定义view动画既简单又复杂。简单是因为只需继承Animation这个抽象类,然后重写initialize和applyTransformation方法就好了,在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩阵变换即可,里面经常需要采用Camera来简化矩阵变换的过程。复杂是因为自定义view动画的过程主要是矩阵变换的过程,需要有线性代数的功底。
我们就来个例子吧,谷歌 APIDemo 里的自定义3d动画Rotate3dAnimation。围绕y轴旋转并且同时沿着z轴平移从而实现类似于3d的效果。先上效果图,效果图如下。
代码量很少,如下图。
public class Rotate3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
float scale = 1; // <------- 像素密度
/**
* 创建一个绕y轴旋转的3D动画效果,旋转过程中具有深度调节,可以指定旋转中心。
* @param context <------- 添加上下文,为获取像素密度准备
* @param fromDegrees 起始时角度
* @param toDegrees 结束时角度
* @param centerX 旋转中心x坐标
* @param centerY 旋转中心y坐标
* @param depthZ 最远到达的z轴坐标
* @param reverse true 表示由从0到depthZ,false相反
*/
public Rotate3dAnimation(Context context, float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
// 获取手机像素密度 (即dp与px的比例)
scale = context.getResources().getDisplayMetrics().density;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
// 调节深度
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
// 绕y轴旋转
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
// 修正失真,主要修改 MPERSP_0 和 MPERSP_1
float[] mValues = new float[9];
matrix.getValues(mValues); //获取数值
mValues[6] = mValues[6]/scale; //数值修正
mValues[7] = mValues[7]/scale; //数值修正
matrix.setValues(mValues); //重新赋值
// 调节中心点
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
使用:我这里是在fragment里写的,布局文件这里没贴出来,因为就是一个简单的ImageView。
ImageView imageView = view.findViewById(R.id.iv);
//括号内参数分别为(上下文,开始角度,结束角度,x轴中心点,y轴中心点,深度,是否扭曲)
final Rotate3dAnimation rotation = new Rotate3dAnimation(Objects.requireNonNull(getContext()),
0, 360, 150, 75, 0f, true);
rotation.setDuration(3000); //设置动画时长
rotation.setRepeatCount(5); //设置重复次数
rotation.setFillAfter(true); //保持旋转后效果
rotation.setInterpolator(new LinearInterpolator()); //设置插值器
imageView.startAnimation(rotation);
七.帧动画
帧动画也是属于View动画。帧动画是顺序播放一组预先定义好的图片。系统提供了单独一个类AnimationDrawable来使用帧动画。帧动画使用比较简单,通过XML来定义一个AnimationDrawable,其地址为:res/drawable/***.xml。
在drawable目录下新建一个以根节点为animaion-list的xml文件。一个item对应一帧图片。android:oneshot="true",只播放一次,android:oneshot="false"无限循环,如果不设置则值默认为false无限循环。代码如下图所示。
将上述的drawable作为View的背景并通过Draw able来播放动画即可。使用如下图。
View v = findViewById(R.id.iv);
v.setBackgroundResource(R.drawable.animation_frame);
AnimationDrawable animationDrawable = (AnimationDrawable) v.getBackground();
animationDrawable.start();
帧动画使用简单,但是注意图片不要太大,否则容易引起OOM。
八.View动画的特殊使用场景
View动画除了上面介绍的四种以外,还可以在一些特殊的场景下使用,比如:
在ViewGroup中控制子元素的出场效果。
在Activity中实现不同Activity之间的切换。
1.ViewGroup中控制子元素的出场效果——LayoutAnimation
LayoutAnimaton作用于ViewGroup,为View Group指定一个动画,这样当它的子元素出场时都会具有这种动画效果。
LayoutAnimaton有四个属性:animationOrder,animation,delay,interpolator。
- animationOrder:有三个选项:normal,reverse,random。normal表示顺序显示(即排在前面的子元素先开始播放入场动画),reverse表示逆向显示(即排在后面的子元素先开始播放入场动画),randow表示随机播放入场动画。
- animation:为子元素指定具体的入场动画。
- delay:表示子元素开始动画的时间延迟。即delay=第一个子元素动画时间/第二个子元素动画时间。怎么理解呢,打个比方,如果delay的值为0.5,那么第一个子元素动画执行到一半的时候,第二个子元素动画开始;如果delay的值为1,那么第一个子元素动画执行完后才开始紧接着执行;
- interpolator:插值器,控制动画变化的速率,不写则默认为是加速减速插值器。
我们有个经典的例子——RecycleView的item都以一定的动画的形式出现。我们先上效果图。
我们现在就开始写RecycleView的item的动画啦,关键步骤和代码如下:
1.在res/anim目录中新建一个根节点为set的anim_recycle_view_item.xml文件,这里将其作为子元素入场动画(每个Item的动画),代码如下图。
2.在res/anim目录中新建一个根节点为layoutAnimation 的anim_recycle_view.xml文件,在里面引用步骤1写好的子元素动画即可。代码如下图。
3.给RecycleView添加layoutAnimation有2种方式,布局中添加或者java代码添加。
- 方式一:在布局文件中对RecycleView添加layoutAnimation属性,引用步骤2写好的layoutAnimation即可,代码如下:
- 方式二:在java代码中对RecycleView添加layoutAnimation属性,引用步骤2写好的layoutAnimation即可,代码如下:
RecyclerView recyclerView=findViewById(R.id.recycleView);
Animation animation= AnimationUtils.loadAnimation(this,R.anim.recycle_view);
LayoutAnimationController controller=new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
recyclerView.setLayoutAnimation(controller);
4.其它代码就是正常的RecycleView的使用了,比如java代码和item的布局代码这里就不贴啦。现在运行一下,就可以看见跟效果图的一样的动画啦。
2.Activity的切换效果
Activity是有默认的切换效果,我们也可以自定义这个Activity的切换效果,主要用到overridePendingTransition(int enterAnim,int exitAnim)这个方法,但是该方法必须写在在startActivity(Intent intent)或者finish()之后才能生效,不然动画效果将不起作用。
- enterAnim——Activity被打开时,所需要的动画资源id。
- exitAnim——Activity被暂停时所需要的动画资源id。
启动Activity时添加自定义的切换效果,代码如下:
Intent intent = new Intent(this, DetailActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
退出Activity时添加自定义的切换效果,代码如下:
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}