Android View加入动画之后使其对用户更加友好,用户体验也得到极大的增强,特别是Android 3.0之后,加入的动画新成员——属性动画,使其更加具备交互的特性,而且通过动画可以做出各种比较炫比较酷的效果。如果没有动画,那么View的表现比较生硬,给用户的体验很不友好。Android动画使其可以做到可以和iphone一样的友好体验,在Android5.0之后,加入了Android Material Design,其用户体验得到了更加友好的发挥,甚至超过了iphone,所以Android动画是学习Android的重点之一。
Android动画可以分为三种:View动画、帧动画和属性动画,上面已经提到了属性动画是Android API11之后新增的,如果想要兼容API11以下的版本,可以使用NineOldAndroids动画库。
View动画
View动画是通过View的平移、旋转、缩放和改变透明度来实现动画,这也是一种渐进式动画。
View动画原理:每次绘制视图时,View的所在ViewGroup中的drawChild函数获取该View的Animation的Transformation值,然后调用canvas.concat(TransformToApply .getMatrix()),然后通过矩阵运算完成动画帧,如果动画没有完成,就继续调用invalidate()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。
View动画提供了TranslateAnimation、RotateAnimation、ScaleAnimation和AlphaAnimation四种动画。通过这四种动画基本上能够完成所有的View的特效效果,不过View动画明显的缺点就是不具备交互性。而Android3.0新增的属性动画就具有交互性。
TranslateAnimation
TranslateAnimation也就是平移动画,显然该动画能够将View在水平或者垂直方向上进行移动。
如在X轴上移动200px
/**
* translateAnimation
* @return
*/
private Animation getTranslate(){
/*TranslateAnimation(int fromXType, float fromXValue,
int toXType, float toXValue,
int fromYType,float fromYValue,
int toYType, float toYValue)
*/
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 200,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0);
translateAnimation.setDuration(300);//动画时间
translateAnimation.setFillAfter(false);//动画结束后View是否保持动画结束时的状态
return translateAnimation;
}
复制代码
启动该动画
TranslateAnimation anim = (TranslateAnimation) getTranslate(); mTargetView.startAnimation(anim);
也可以在xml中写:在项目res下新建一个anim目录,在目录下新建一个.xml文件。
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="0%p"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="100%p"/>
复制代码
android:fromXDelta:起始点X轴坐标,可以是数值、百分数、百分数p 三种样式,比如 30、30%、30%p。 android:fromYDelta:起始点Y轴从标,可以是数值、百分数、百分数p 三种样式; 当为数值时,表示在当前View的左上角,即原点处加上50px; 如果是50%,表示在当前控件的左上角加上自己宽度的50%; 如果是50%p,表示在当前的左上角加上父控件宽度的50%。 android:toXDelta:结束点X轴坐标 android:toYDelta:结束点Y轴坐标
使用:
TranslateAnimation anim = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.my_animation); mTargetView.startAnimation(anim);
RotateAnimation
RotateAnimation旋转动画,能够将View进行旋转,达到动画的效果。
/**
* 围绕自身中点
* 旋转270度
*
* @return
*/
private Animation getRotate() {
RotateAnimation rotateAnimation = new RotateAnimation(
0, 270,//开始角度和旋转的到的度数
Animation.RELATIVE_TO_SELF, 0.5f,//x
Animation.RELATIVE_TO_SELF, 0.5f);//y
rotateAnimation.setDuration(1000);
rotateAnimation.setFillAfter(true);
return rotateAnimation;
}
复制代码
在xml中表示:
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="270"
android:pivotX="50%"
android:pivotY="50%"
android:duration="300"
android:fillAfter="true"/>
复制代码
ScaleAnimation
ScaleAnimation对View进行缩放的动画。
/**
* 缩放
*
* @return
*/
private Animation getScaleAnim() {
/* (float fromX, float toX,
float fromY, float toY,
int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue)*/
ScaleAnimation scaleAnimation = new ScaleAnimation(
1.0f, 0.0f, 1.0f, 0.0f,//xy轴上的缩放起始值
Animation.RELATIVE_TO_SELF, 0.5f,//x轴的缩放中心
Animation.RELATIVE_TO_SELF, 0.5f);//y轴的缩放中心
scaleAnimation.setDuration(300);
scaleAnimation.setFillAfter(false);
return scaleAnimation;
}
复制代码
在xml中表示:
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.0"
android:toYScale="0.0"
android:duration="300"
android:fillAfter="true"/>
复制代码
android:fromXScale:起始的X方向上相对自身的缩放比例; android:toXScale:结尾的X方向上相对自身的缩放比例,浮点值; android:fromYScale:起始的Y方向上相对自身的缩放比例,浮点值, android:toYScale:结尾的Y方向上相对自身的缩放比例,浮点值; android:pivotX:缩放起点X轴坐标,可以是数值、百分数、百分数p 三种样式; android:pivotY:缩放起点Y轴坐标,取值及意义跟android:pivotX一样。
ScaleAnimation
AlphaAnimation是可以对View进行透明度变化的动画。
/**
* 改变透明度动画
* @return
*/
private Animation getAlpha(){
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
alphaAnimation.setDuration(300);
alphaAnimation.setFillAfter(true);
return alphaAnimation;
}
复制代码
在xml中表示:
<?xml version="1.0" encoding="utf-8"?>
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="300"
android:fillAfter="true"/>
复制代码
以上便是View动画提供了TranslateAnimation、RotateAnimation、ScaleAnimation和AlphaAnimation四种动画,上文中的动画已经经过验证的了。但是上文中的动画都是单一形式的,那么有没有提供一种方法,能够混合一起播放以上View动画提供的四种动画的呢?答案是:当然有,那就是使用AnimationSet动画集合。
网上也有相当多的文章是关于AnimationSet的使用和介绍的,那么什么是AnimationSet呢?其实就是一个动画集合,能够将View动画的提供的几种动画可以一起播放,从而产生更炫的动画效果。AnimationSet也可以在xml文件中设置,其对应的标签就是标签。
AnimationSet set = new AnimationSet(false);
ScaleAnimation anim01 = (ScaleAnimation) getScaleAnim();
RotateAnimation anim02 = (RotateAnimation) getRotate();
AlphaAnimation anim03 = (AlphaAnimation) getAlpha();
set.addAnimation(anim01);
set.addAnimation(anim02);
set.addAnimation(anim03);
set.setDuration(300);
mTargetView.startAnimation(set);
复制代码
在xml中表示:
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fillAfter="true">
<alpha
android:toAlpha="0.0"
android:fromAlpha="1.0"/>
<rotate
android:fromDegrees="0"
android:toDegrees="270"
android:pivotX="50%"
android:pivotY="50%"/>
<scale
android:pivotY="50%"
android:pivotX="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0.0"
android:toYScale="0.0"/>
</set>
复制代码
set的属性有:
android:duration:动画持续时间,以毫秒为单位 ;
android:fillAfter:如果设置为true,控件动画结束时,将保持动画最后时的状态;
android:fillBefore:如果设置为true,控件动画结束时,还原到开始动画前的状态;
android:fillEnabled:与android:fillBefore效果相同,都是在动画结束时,将控件还原到初始化状态;
android:repeatCount 重复次数;
android:repeatMode:重复类型,有reverse和restart两个值,reverse表示倒序回放,restart表示重新放一遍,必须与repeatCount一起使用才能看到效果。因为这里的意义是重复的类型,即回放时的动作;
android:interpolator:设定插值器。
使用:
AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(this, R.anim.my_animation); mTargetView.startAnimation(set);
上文中就是View动画的种类和集合的基本用法,除了以上的知识还有一个重要的知识点就是动画的监听。动画的监听可实现很多需求,比如在动画开始或者结束后要做的事情,或者在动画不断回调的方法onAnimationRepeat中实现一些动画不能实现的效果。
添加动画监听很简单,如下所示:
set.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
复制代码
自定义属性动画
在View动画中除了上文中的四种动画之外,其实自定义动画也算是View动画的一种,自定义需要继承Animation类,并重写覆盖initialize和applyTransformation方法。在initialize方法中主要是做一些初始化的工作,applyTransformation通过矩阵变换来实现动画效果,一般采用Camera来简化矩阵变换过程。
applyTransformation(float interpolatedTime, Transformation t)方法有两个参数 interpolatedTime:动画当前完成的百分比和当前时间所对应的插值所计算得来的,取值0到1.0,也就是插值器的时间因子。
Transformation :矩阵的封装类,可以使用这个类获得当前的矩阵对象
Matrix matrix = t.getMatrix();
然后通过矩阵变化实现动画。
public class MyAnimation extends Animation {
private float mWidth;
private float mHeight;
private Camera mCamera;
private float mRorateX = 270.0f;
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
setDuration(300);
setFillAfter(true);
mWidth = width/2;
mHeight = height/2;
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
Matrix matrix = t.getMatrix();
mCamera.save();
mCamera.rotateX(mRorateX*interpolatedTime);
mCamera.getMatrix(matrix);
mCamera.restore();
//通过pre方法设置矩阵作用前的偏移量来改变旋转中心
matrix.preTranslate(mWidth,mHeight);
matrix.postTranslate(-mWidth,-mHeight);
}
}
复制代码
以上代码就是沿着X轴方向旋转。
帧动画
帧动画是顺序播放一组预先定义好的图片,类似电影,逐帧播放图片。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/loadingmore01" android:duration="30"/>
<item android:drawable="@drawable/loadingmore02" android:duration="30"/>
<item android:drawable="@drawable/loadingmore03" android:duration="30"/>
<item android:drawable="@drawable/loadingmore04" android:duration="30"/>
<item android:drawable="@drawable/loadingmore05" android:duration="30"/>
<item android:drawable="@drawable/loadingmore06" android:duration="30"/>
<item android:drawable="@drawable/loadingmore07" android:duration="30"/>
<item android:drawable="@drawable/loadingmore08" android:duration="30"/>
<item android:drawable="@drawable/loadingmore09" android:duration="30"/>
<item android:drawable="@drawable/loadingmore10" android:duration="30"/>
<item android:drawable="@drawable/loadingmore11" android:duration="30"/>
<item android:drawable="@drawable/loadingmore12" android:duration="30"/>
<item android:drawable="@drawable/loadingmore13" android:duration="30"/>
<item android:drawable="@drawable/loadingmore14" android:duration="30"/>
<item android:drawable="@drawable/loadingmore15" android:duration="30"/>
<item android:drawable="@drawable/loadingmore16" android:duration="30"/>
<item android:drawable="@drawable/loadingmore17" android:duration="30"/>
<item android:drawable="@drawable/loadingmore18" android:duration="30"/>
<item android:drawable="@drawable/loadingmore19" android:duration="30"/>
<item android:drawable="@drawable/loadingmore20" android:duration="30"/>
<item android:drawable="@drawable/loadingmore21" android:duration="30"/>
<item android:drawable="@drawable/loadingmore22" android:duration="30"/>
<item android:drawable="@drawable/loadingmore23" android:duration="30"/>
<item android:drawable="@drawable/loadingmore24" android:duration="30"/>
<item android:drawable="@drawable/loadingmore25" android:duration="30"/>
<item android:drawable="@drawable/loadingmore26" android:duration="30"/>
<item android:drawable="@drawable/loadingmore27" android:duration="30"/>
</animation-list>
复制代码
使用
animView = new ImageView(context); animView.setImageResource(R.drawable.anim_loading_more); animationDrawable = (AnimationDrawable) animView.getDrawable(); animationDrawable.start();
帧动画的使用比较简单,以上就是定义一个帧动画并使用。
属性动画
属性动画是Android API11新加入的动画,属性动画能够对任何对象做动画,只要对象有这个属性,并且提供get和set方法即可,因为是API11才加入的动画,所以如果想向下兼容,可以使用nineoldandroids动画库来兼容。
属性动画提供了ValueAnimator、ObjectAnimator和AnimatorSet,通过他们可以使对象实现炫丽的动画效果。属性动画有一个最大的特点就是其可以实现与用户交互,这也是其他动画所不具备的特点。
ObjectAnimator
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView,"translationY",0,2000,0); anim.start();
这是ObjectAnimator的简单用法,但是已经显示了动画效果。
ofFloat方法的参数分析:ofFloat(Object target, String propertyName, float... values) 第一个参数:动画对象; 第二个参数:对象的属性,要执行动画的属性,这里我改变的是Y轴上的移动距离; 第三个参数:可变参数,可以有多个值,即对象属性要执行动画的值。上例中表示的是属性动画在Y轴上先移动到2000px的位置,然后再执行动画移动到初始位置。
注意:这里的ObjectAnimator动画并没有指定执行时间,是因为属性动画的默认时间间隔300ms,默认帧率为10ms/帧。
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView,"translationY",0,2000,-200,0); anim.setDuration(500); anim.start();
上例执行的动画会有弹动的效果,动画执行translationY,先移动到2000px的位置,然后再移动-200px的位置,最后回到初始的位置。而且设定了动画执行的时间。
旋转动画:
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView,"rotation",0,360,-200,0);
anim.setDuration(2000);
anim.start();
复制代码
改变透明度:
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView,"alpha",1.0f,0.0f,0.8f);
anim.setDuration(2000);
anim.start();
复制代码
缩放:
ObjectAnimator anim = ObjectAnimator.ofFloat(mTargetView, "scaleX",1,0,1);
anim.setDuration(2000);
anim.start();
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mTargetView, "scaleY",1,0,1);
anim1.setDuration(2000);
anim1.start();
复制代码
第三参数表示缩放的倍数。
改变背景颜色
ObjectAnimator anim = ObjectAnimator.ofInt(mStart, "backgroundColor",0xff0000,0xffff8080,0xff000000);
anim.setDuration(2000);
anim.start();
复制代码
ObjectAnimator 同时执行多个属性动画:
PropertyValuesHolder px = PropertyValuesHolder.ofFloat("scaleX",1,0,1);
PropertyValuesHolder py = PropertyValuesHolder.ofFloat("scaleY",1,0,1);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTargetView, px,py);
anim.setDuration(2000);
anim.start();
复制代码
ValueAnimator
ValueAnimator只针对值,只是对值做动画运算,而不是针对控件,没有跟任何的控件相关联,需要监听ValueAnimator的动画过程来自己对控件做操作。
ValueAnimator anim = ValueAnimator.ofFloat(0,2000,-200,0);
anim.setDuration(2000);
anim.start();
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int curValueFloat = (int)valueAnimator.getAnimatedValue();
//layout(int l, int t, int r, int b)
Log.i("tag","value"+curValueFloat);
}
});
复制代码
读者可以参考自定义控件三部曲之动画篇(四)——ValueAnimator基本使用和 Android 属性动画(Property Animation) 完全解析 (上)这两篇文章,就已足够了解ValueAnimator了。
AnimatorSet
AnimatorSet是属性动画的集合,可以给View设置一组的属性动画,也可以指定播放顺序,是否一起播放或者是否延迟播放。
PropertyValuesHolder px = PropertyValuesHolder.ofFloat("scaleX",1.0f,0.0f,1.0f);
PropertyValuesHolder py = PropertyValuesHolder.ofFloat("scaleY",1.0f,0.0f,1.0f);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTargetView, px,py);
ObjectAnimator scale = ObjectAnimator.ofFloat(mTargetView,"rotation",0.0f,360.0f,-360.0f);
AnimatorSet set = new AnimatorSet();
set.setDuration(2000);
set.playTogether(anim,scale);//一起播放
set.start();
复制代码
使用playTogether方法,可以设置一起播放设置进去的动画。也可以使用以下语句设置一起播放动画
set.play(anim).with(scale);
按顺序播放动画:
set.playSequentially(anim,scale);
除了使用playSequentially,还可以使用如下语句来设置按顺序播放:
set.play(scale).after(anim);
动画监听AnimatorListener
ObjectAnimator translate = ObjectAnimator.ofFloat(goDescri, "translationX", -50);
translate.setInterpolator(new AccelerateInterpolator());
translate.setDuration(1500);
ObjectAnimator alpha1 = ObjectAnimator.ofFloat(goDescri, "alpha", 0.0f,1.0f);
alpha1.setInterpolator(new AccelerateInterpolator());
alpha1.setDuration(1000);
ObjectAnimator alpha2 = ObjectAnimator.ofFloat(goDescri, "alpha", 1.0f,0.0f);
alpha2.setInterpolator(new AccelerateInterpolator());
alpha2.setDuration(500);
AnimatorSet mAnimatorSet = new AnimatorSet();
mAnimatorSet.setInterpolator(new AccelerateInterpolator());
mAnimatorSet.play(translate).with(alpha1);
mAnimatorSet.play(alpha2).after(alpha1);
mAnimatorSet.addListener(new Animator.AnimatorListener()
{
@Override
public void onAnimationStart(Animator animation)
{
}
@Override
public void onAnimationEnd(Animator animation)
{
mAnimatorSet.start();
}
@Override
public void onAnimationCancel(Animator animation)
{
}
@Override
public void onAnimationRepeat(Animator animation)
{
}
});
复制代码
上述代码通过监听AnimatorListener,在动画结束时重新启动动画,而从达到动画顺序循环播放的效果。
循环播放还可以如下设置:
PropertyValuesHolder px = PropertyValuesHolder.ofFloat("scaleX",1.0f,0.0f,1.0f);
PropertyValuesHolder py = PropertyValuesHolder.ofFloat("scaleY",1.0f,0.0f,1.0f);
ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mTargetView, px,py);
anim.setRepeatCount(ObjectAnimator.INFINITE);//播放次数
anim.setRepeatMode(ObjectAnimator.REVERSE);//播放顺序,顺序和倒叙
ObjectAnimator scale = ObjectAnimator.ofFloat(mTargetView,"rotation",0.0f,360.0f,-360.0f);
scale.setRepeatCount(ObjectAnimator.INFINITE);
scale.setRepeatMode(ObjectAnimator.REVERSE);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(scale,anim);
set.start();
复制代码
通过设置播放次数anim.setRepeatCount(ObjectAnimator.INFINITE); 并且设置播放顺序anim.setRepeatMode(ObjectAnimator.REVERSE);来达到循环播放动画,这两个函数在AnimatorSet时没有。
插值器和估值器
插值器(Interpolator)可以分为时间插值器(TimeInterpolator)、线性插值器(LinearInterpolator)、加速减速插值器(AccelerateDecerateInterpolator)、减速插值器(DecerateInterpolator)。
时间插值器:根据时间流逝的百分比计算出当前属性值改变的百分比; 线性插值器:使动画匀速; 加速减速插值器:使动画两头慢,中间快; 减速插值器:使动画越来越慢。
时间插值器
根据时间流逝的百分比计算出当前属性值改变的百分比
AnimatorSet set = new AnimatorSet();
set.setDuration(2000);
set.playTogether(scale,anim);
set.setInterpolator(new TimeInterpolator() {
@Override
public float getInterpolation(float v) {
Log.i("tag","v----------->"+v);
return v;
}
});
复制代码
加速减速估值器
AnimatorSet set = new AnimatorSet();
set.setDuration(2000);
set.playTogether(scale,anim);
set.setInterpolator(new AccelerateDecelerateInterpolator());
set.start();
复制代码
自定义估值器
public class DecelerateAccelerateInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
float result;
if (input <= 0.5) {
result = (float) (Math.sin(Math.PI * input)) / 2;
} else {
result = (float) (2 - Math.sin(Math.PI * input)) / 2;
}
return result;
}
}
复制代码
以上自定义插值器实现了先减速后加速。
使用
set.setInterpolator(new DecelerateAccelerateInterpolator());
读者可以阅读这篇文章,此文章详细的介绍了插值器和估值器[Android 动画:你真的会使用插值器与估值器吗?(含详细实例教学)(http://www.jianshu.com/p/2f19fe1e3ca1)
估值器
估值器(TypeEvaluator):根据当前属性的改变的百分比来计算改变后的属性值,系统预置的估值器有:IntEvaluator(针对整形属性)、FloatEvaluator(针对浮点型属性)和ArgbEvaluator(针对Color属性)。
对任意对象属性做动画
既然属性动画能够对任何对象做动画,只要对象有这个属性,并且提供get和set方法即可。 如果这个对象没有get和set方法,怎么办呢? 1)给对象加上get和set方法; 2)用个类来包装原始对象,提供get和set方法;
private static class ViewWrapper{
private View target;
public ViewWrapper(View target) {
this.target = target;
}
public int getWidth(){
return target.getLayoutParams().width;
}
public void setWidth(int width){
target.getLayoutParams().width = width;
target.requestLayout();
}
}
复制代码
使用
ViewWrapper wrapper = new ViewWrapper(mTargetView); ObjectAnimator.ofInt(wrapper,"width",2000).start();
除了以上的方法外还可以使用ValueAnimator来监听值的改变,然后做改变,达到动画的效果。
关于这一点知识,读者可以参考《Android开发艺术探索》的第七章《Android动画深入分析》。
ViewAnimationUtils
Android5.0新特性增加了一种动画框架,这就是ViewAnimationUtils,我们可以利用ViewAnimationUtils来做一些动画效果,比如水波纹,揭露等动画效果。
public final class ViewAnimationUtils {
private ViewAnimationUtils() {}
public static Animator createCircularReveal(View view,
int centerX, int centerY, float startRadius, float endRadius) {
return new RevealAnimator(view, centerX, centerY, startRadius, endRadius);
}
}
复制代码
上面代码就是ViewAnimationUtils类全部代码,该动画工具只有createCircularReveal方法来创建动画效果。从方法中,可以知道最后交给了RevealAnimator类来完成动画效果。
createCircularReveal方法说明:
view:动画作用的View;
centerX:扩散的中心点的X轴坐标;
centerY:扩散的中心点的Y轴坐标;
startRadius:开始扩散初始半径;
endRadius:扩散结束半径;
使用:
Animator animator = ViewAnimationUtils.createCircularReveal(mStart, mStart.getWidth()/2, mStart.getHeight()/2, 0, mStart.getHeight());
animator.setDuration(1000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
复制代码
上面代码就是mStart做水波纹扩散的效果。
mStart的揭露动画:
Animator animator = ViewAnimationUtils.createCircularReveal(btn, 0, 0, 0, (float)Math.hypot(btn.getWidth(), btn.getHeight()));
animator.setDuration(1000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
复制代码
转场动画
Android的转场动画分为两种:普通转场和共享元素转场。
普通转场
普通转场通过overridePendingTransition设置Activity的关闭和显示动画,如下所示:
startActivity(new Intent(this, Animator2Activity.class));
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
复制代码
通过以上代码,跳转Activity时:当前的Activity动画淡出,新的Activity淡入,完成淡出淡入的Activity转场动画。
Activity退出时同样可以设置转场动画
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
复制代码
这是API21系统自带的转场动画,只要在API21或者以上才有。 fade_in:
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@interpolator/decelerate_quad"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_longAnimTime" />
复制代码
fade_out:
<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/accelerate_quad"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="@android:integer/config_mediumAnimTime"
/>
复制代码
滑入滑出转场
startActivity(new Intent(this, Animator2Activity.class));
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right);
复制代码
除了以上系统自带的转场动画,也可以在res的anim目录下定义动画xml来实现自定义的转场动画。
scale_in.xml
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromXScale="0%"
android:fromYScale="0%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="100%"/>
复制代码
scale_out.xml
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromXScale="100%"
android:fromYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0%"
android:toYScale="0%"/>
复制代码
使用的时候直接在overridePendingTransition指定动画即可
startActivity(new Intent(this, Animator2Activity.class));
overridePendingTransition(R.anim.scale_in, R.anim.scale_out);
复制代码
底部滑入滑出 slide_in_botton.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromYDelta="100%"
android:toYDelta="0%"
/>
复制代码
slide_out_botton.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromYDelta="0%"
android:toYDelta="100%"
/>
复制代码
共享元素转场
共享元素转场是指:可以把两个Activity当中的相同的元素关联起来做连贯的变换动画。 共享元素转场是Android5.0或以上才显示的,而使用共享元素转场需要条件: A、必须给两个Activity设置Window.FEATURE_CONTENT_TRANSITIONS,让Activity允许使用转场动画。 而设置Window.FEATURE_CONTENT_TRANSITIONS有两个方法: 1)通过在setContentView方法之前设 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); 2)在主题修改 true
B、给两个Activity当中的共享元素view都设置同一个名字(android:transitionName)
共享元素分为单共享元素和多个共享元素。
单共享元素
public class AnimationActivity extends AppCompatActivity implements View.OnClickListener {
private Button mStart;
private ImageView mTargetView;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setContentView(R.layout.activity_animation);
initView();
}
private void initView() {
mStart = (Button) this.findViewById(R.id.animation_btn);
mStart.setOnClickListener(this);
btn = (Button) this.findViewById(R.id.animation_btn01);
btn.setOnClickListener(this);
mTargetView = (ImageView) this.findViewById(R.id.animation_target);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onClick(View view) {
if (view == mStart) {
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(
this, mTargetView, "transition_iv");
Intent intent = new Intent(this, Animator2Activity.class);
startActivity(intent, optionsCompat.toBundle());
} else if (view == btn) {
}
}
}
复制代码
设置getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); 然后使用ActivityOptionsCompat.makeSceneTransitionAnimation来创建ActivityOptionsCompat对象。
makeSceneTransitionAnimation方法已经做好版本判断了,我们看下该方法的参数。
(Activity activity,View sharedElement, String sharedElementName) activity:当前activity的对象 sharedElement:共享元素的View sharedElementName:也就是在sharedElement中设置android:transitionName名字。
activity_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
tools:context="com.main.animation.AnimationActivity">
<Button
android:id="@+id/animation_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="startAnimation"/>
<Button
android:id="@+id/animation_btn01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/animation_btn"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="Text"/>
<ImageView
android:id="@+id/animation_target"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_alignParentBottom="true"
android:transitionName="transition_iv"
android:layout_centerHorizontal="true"
android:scaleType="centerCrop"
android:src="@drawable/animationtarget"/>
</RelativeLayout>
复制代码
在xml中需要注意的就是贡献元素的View需要设置 android:transitionName。
跳转的Activity
public class Animator2Activity extends AppCompatActivity {
private ImageView target;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置允许使用转场动画
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setContentView(R.layout.activity_animator2);
target = (ImageView)this.findViewById(R.id.animation2_target);
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
}
复制代码
同样需要设Window.FEATURE_CONTENT_TRANSITIONS。
activity_animator2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.main.animation.Animator2Activity">
<ImageView
android:id="@+id/animation2_target"
android:layout_width="match_parent"
android:layout_height="500dp"
android:scaleType="centerCrop"
android:src="@drawable/animationtarget"
android:transitionName="transition_iv"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="0dp"/>
</RelativeLayout>
复制代码
同样需要在共享的View设置android:transitionName,而且名字需要相同。
说明: 按返回键的时候自动实现了返回的共享元素转场动画,具体原因看源码:
public void onBackPressed() {
finishAfterTransition();
}
public void finishAfterTransition() {
if (!mActivityTransitionState.startExitBackTransition(this)) {
finish();
}
}
复制代码
当然你可以自己调用finishAfterTransition()来结束activity,也是有动画。
多元素共享转场
基本上跟单元素转场一样,唯一的不同点就是使用Pair.create来设置多个元素转场。
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat
.makeSceneTransitionAnimation(this, Pair.create((View)mTargetView, "transition_iv"),
Pair.create((View)text, "transition_text"));
Intent intent = new Intent(this, Animator2Activity.class);
startActivity(intent, optionsCompat.toBundle());
复制代码
以上就是多元素共享转场的核心代码。
使用RecyclerView实现共享元素转场动画。
TranslationActivity:
public class TranslationActivity extends AppCompatActivity {
private RecyclerView mList;
private List<String> mDatas = new ArrayList<>();
private MyAdapter mMyAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_translation);
mList = (RecyclerView)this.findViewById(R.id.translation_list);
mList.setLayoutManager(new LinearLayoutManager(this));
for (int i = 0; i < 30; i++) {
String tx = "呵呵呵"+i;
mDatas.add(tx);
}
mMyAdapter = new MyAdapter(this,mDatas);
mList.setAdapter(mMyAdapter);
mMyAdapter.setOnItemOnclickListener(new MyAdapter.OnItemOnclickListener() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onClickItem(ImageView iv, TextView tv, String text) {
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat
.makeSceneTransitionAnimation(TranslationActivity.this, Pair.create((View)iv, "transition_iv"),
Pair.create((View)tv, "transition_text"));
Intent intent = new Intent(TranslationActivity.this, Animator2Activity.class);
intent.putExtra("tag",text);
startActivity(intent, optionsCompat.toBundle());
}
});
}
private static class MyAdapter extends RecyclerView.Adapter{
private List<String>datas;
private Context mContext;
private OnItemOnclickListener mOnItemOnclickListener;
public void setOnItemOnclickListener(OnItemOnclickListener onItemOnclickListener) {
mOnItemOnclickListener = onItemOnclickListener;
}
public MyAdapter(Context context,List<String> datas) {
this.datas = datas;
mContext = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.transition_item,null);
return new MyHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
MyHolder myHolder = (MyHolder)holder;
myHolder.text.setText(datas.get(position));
myHolder.tag = datas.get(position);
}
@Override
public int getItemCount() {
if (datas != null && datas.size()>0){
return datas.size();
}
return 0;
}
private class MyHolder extends RecyclerView.ViewHolder{
private TextView text;
private String tag;
private ImageView iv;
public MyHolder(View itemView) {
super(itemView);
text = (TextView)itemView.findViewById(R.id.transition_tx);
iv = (ImageView) itemView.findViewById(R.id.transition_iv);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mOnItemOnclickListener != null){
mOnItemOnclickListener.onClickItem(iv,text,tag);
}
}
});
}
}
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
}
};
public interface OnItemOnclickListener{
void onClickItem(ImageView iv,TextView tv,String text);
}
}
}
复制代码
主要是设置RecyclerView,实现RecyclerView.Adapter,然后设置Item的点击,RecyclerView没有为Item提供点击,所以要实现点击回调。 在回调设置共享元素转场动画
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat
.makeSceneTransitionAnimation(TranslationActivity.this, Pair.create((View)iv, "transition_iv"),Pair.create((View)tv, "transition_text"));
Intent intent = new Intent(TranslationActivity.this, Animator2Activity.class);
intent.putExtra("tag",text);
startActivity(intent, optionsCompat.toBundle());
复制代码
注意: 回调的时候需要把Item的共享元素作为参数传递过来,否则设置不了。
translation_list.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical"
tools:context="com.main.animation.TranslationActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/translation_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
复制代码
Item条目的布局transition_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp">
<ImageView
android:id="@+id/transition_iv"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginTop="10dp"
android:scaleType="centerCrop"
android:src="@drawable/animationtarget"
android:transitionName="transition_iv"/>
<TextView
android:id="@+id/transition_tx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="tv"
android:textColor="#ffffff"
android:textSize="30dp"
android:transitionName="transition_text"/>
</RelativeLayout>
复制代码
同样共享元素需要设置android:transitionName。
这里的跳转的页面直接跳转到上面的Animator2Activity页面。
效果界面:
由于没有录制视频,所以读者很难看出效果,不过读者可以根据上面代码,自行实战一把,同时我的也希望读者可以自己动手实战,巩固和理解该知识点。