以前写动画也是零零种种,需要的时候就查API或找现成的,不够系统。现在通过学习将Android整个动画体系勾勒出来,做到有的放矢。 安卓框架提供了2个动画系统:属性动画(Android 3.0)和View动画。这两种动画系统都是可行的,但是,在一般情况下,属性动画是首选的方法,因为它是更灵活,提供更多的功能。除了这两个系统,你可以利用Drawable动画,它允许你一帧一个的加载显示Drawable资源。所以总体来说Android API提供了三类的动画: - Tween动画或View动画(API 1.0开始) - Drawable动画或Frame动画、帧动画 - Property动画:Android 3.0(API 11)以上版本可用,Android推荐。
Android有关动画的类在android.view.animation包中,抽象类Animation有很多子类,以完成不同的动画效果。
Animation子类动画效果AlphaAnimation渐变透明度动画效果RotateAnimation旋转动画ScaleAnimation尺寸缩放动画TranslateAnimation位移动画AnimationSet动画容器Tween动画结束时View的布局属性并未发生变化
Animation类中有一个内部动画监听类AnimationListener,通过它主要实现动画开始、进行到结束的事件监听。它有三个方法void onAnimationStart(Animation animation);、void onAnimationRepeat(Animation animation);、void onAnimationEnd(Animation animation);。
在res/anim目录下定义动画文件,然后在代码中AnimationUtilsd.loadAnimation(context,animId)来调用。
View动画或Tween动画在Android比较老的系统中就有了,它仅适用于View。它也比较容易设置,可以提供足够的能力,以满足许多应用的需求。
& 3种插值器:accelerate_decelerate_interpolator 加速-减速 动画插入器 accelerate_interpolator 加速-动画插入器 decelerate_interpolator减速- 动画插入器
*数值或百分数,如50表示View的左上角50个像素为起始点,50%表示以当前View的左上角加上当前View的50%宽高作为起始点,50%p表示以当前View的左上角加父控件的50%的宽高为起始点。在XML中可以方便的用%号来表示,那么在代码中,使用pivotType表示,有三种:Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF,和 Animation.RELATIVE_TO_PARENT
Tween动画是通过ParentView来不断调整ChildView的画布坐标系来实现的。
applyTransformation(float interpolatedTime, Transformation t)Transformation里面有一个矩阵Matrix,和Alpha。实现自己的动画,也是继承Animation,然后重写applyTransformation方法。 根据这个原理,定制自己的Tween动画:借助android.graphics.Camera类。可实现 - 3D翻转动画 - 扭曲动画 - 抛物线
可以通过在XML中定义多个动画,执行时里面的动画是同时执行的。
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <alpha/> <scale/> <translate/> <rotate/> </set> 1234567 1234567Drawable动画,也叫帧动画。它对应的操作类在android.graphics.drawable包下,AnimationDrawable类。有XML方式和Java代码两种方式。
它的属性如下:
属性(方法)方法start()开始播放stop()通知播放addFrame(Drawable frame,int duration)添加一帧,并设置持续时间getDuration(int index)得到指定帧的持续时间getFrame(int index)得到指定帧的drawablegetNumberOfFrames()得到所有帧的数量isOneShot()是否执行一次isRunning()是否正在播放setOneShot(boolean oneShot)设置是否执行一次注意:start()方法不能再Activity的onCreate()中调用,因为那时AnimationDrawable还未完全附着在Window上。如果想最快的运行,可在onWindowFocusChange()方法中调用。
XML中设置:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@mipmap/common_loading3_0" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_1" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_2" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_3" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_4" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_5" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_6" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_7" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_8" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_9" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_10" android:duration="50"/> <item android:drawable="@mipmap/common_loading3_11" android:duration="50"/> </animation-list> 1234567891011121314 1234567891011121314Java中设置:
@TargetApi(16) AnimationDrawable animationDrawable = new AnimationDrawable(); Resources r = getResources(); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_0), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_1), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_2), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_3), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_4), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_5), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_6), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_7), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_8), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_9), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_10), 50); animationDrawable.addFrame(r.getDrawable(R.mipmap.common_loading4_11), 50); animationDrawable.setOneShot(false); if(Build.VERSION.SDK_INT <= 16){ ivAnimDrawableJava.setBackgroundDrawable(animationDrawable); }else{ ivAnimDrawableJava.setBackground(animationDrawable); } animationDrawable.start(); 12345678910111213141516171819202122 12345678910111213141516171819202122另一种AnimtionDrawable,很方便让一个Drawable不停的旋转。
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/vector_drawable" android:pivotX="50%" android:pivotY="50%" android:visible="true"/> 12345 12345Interpolator是一个接口,继承TimeInterpolator接口,TimeInterpolator只有一个抽象方法需要实现:float getInterpolation(float input)。Android系统提供了很多内置的一些插值器,比如: - AccelerateInterpolator:动画从开始到结束,变化率是一个加速的过程。 - DecelerateInterpolator:动画从开始到结束,变化率是一个减速的过程。 - CycleInterpolator:动画从开始到结束,变化率是循环给定次数的正弦曲线。 - AccelerateDecelerateInterpolator:动画从开始到结束,变化率是先加速后减速的过程。 - LinearInterpolator:动画从开始到结束,变化率是线性变化。 LinearInterpolator线形插值器的源码如下:
@HasNativeInterpolator public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { public LinearInterpolator() { } public LinearInterpolator(Context context, AttributeSet attrs) { } public float getInterpolation(float input) { return input; } /** @hide */ @Override public long createNativeInterpolator() { return NativeInterpolatorFactoryHelper.createLinearInterpolator(); } 123456789101112131415161718 123456789101112131415161718插补器的原理就是通过改变实际执行动画的时间点,提前/延迟执行默认的时间点来达到加速/减速的效果。在getInterpolation方法中直接返回input(0.0~1.0f)之间的一个值。input表示正常执行动画的时间点,返回值是用户真正想要它执行的时间点,LinearInterpolator返回input,他们的斜率(比值)是个常数,所以表示匀速,速率不变。我们可以通过在里面设置函数改变动画的速率。如加速插值器里的getInterpolation方法是这样实现的:具体分析参考:详解之android动画interpolator插补器。
public float getInterpolation(float input) { if (mFactor == 1.0f) { return input * input; } else { return (float)Math.pow(input, mDoubleFactor); } } 1234567 1234567我们自定义Interpolator也是这样实现,继承BaseInterpolator,实现getInterpolation方法,在getInterpolation方法中实现自己的插值函数,做出想要的动画效果。
Android 里的 Property 动画是为了弥补之前 View 动画的不足而在 API 11之后引入系统的。Property 动画功能非常强大和灵活,学好、用好 Property 动画将对 App 开发带来很好的效果。 Property动画本质是修改对象属性值实现的动画。在2.3以前的版本中,可以使用NineOldAndroids框架,实现属性动画。
在android.animation包下,抽象类Animator是所有属性动画的超类。 有两个重要的类: - Keyframe:fraction(时间)、value(具体值)、interpolator(插值器)。 - PropertyValueHolder:Property(x位置…)、Keyframe集合。
Animator:
XML属性Java代码说明android:durationsetDuration(long duration)动画时长android:interpolatorsetInterpolator(TimeIntepolator v)插值器android:startOffsetsetStartDelay(long delay)调用start方法后动画延迟时间setTartget(Object o)动画对象ValueAnimator:
XML属性Java代码说明android:repeatModesetRepeatMode(int value)重复模式android:repeatCountsetRepeatCount(int count)重复次数android:valueFrom,\nandroid:valueTo,\nandroid:valueTypeofArgb(int..),\nofint(int …) ,\nofFloat(float…)float,int或color类型,\nfloat,int或color类型,\nintType或floatType,color不用指定setFrameDelay(long delay)多长时间刷新一次ObejctAnimator
XML属性Java代码说明android:propertyNamesetPropertyName()String类型,必须要设置的节点属性注意:动画更新的过程中会不断更新该属性,所以对象比较提供该属性的处理方法(getXXX,setXXX)。 属性:x,y,rotateX,rotateY,traslateX,translateY,alpha,scaleX,scaleY Property动画的原点在对象父容器的0,0位置
AnimationSet
XML属性Java代码说明android:orderingplaySequentially()/playTogether()子动画是先后有序的还是同时进行。在res/animator目录下定义xml(TimeAnimator不支持),然后使用AnimatorInfaltor.loadAnimator(context,animId)。
res/animator/object_set.xml:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="sequentially"> <!-- set中可以嵌套set,先執行這個set中同時执行的动画 --> <set android:ordering="together"> <objectAnimator android:duration="1000" android:propertyName="x" android:valueFrom="0" android:valueTo="250" android:valueType="floatType" /> <objectAnimator android:duration="1000" android:propertyName="y" android:valueFrom="0" android:valueTo="250" android:valueType="floatType" /> <objectAnimator android:duration="1000" android:propertyName="rotation" android:valueFrom="0" android:valueTo="360" /> <objectAnimator android:duration="1000" android:propertyName="rotationX" android:valueFrom="0" android:valueTo="360" /> <objectAnimator android:duration="1000" android:propertyName="rotationY" android:valueFrom="0" android:valueTo="360" /> </set> <objectAnimator android:duration="1000" android:propertyName="scaleX" android:valueFrom="1" android:valueTo="0.5" /><!-- 如果第一次缩小到0.5,第二次再次缩放就没效果,因为属性改了 --> <objectAnimator android:duration="1000" android:propertyName="scaleY" android:valueFrom="1" android:valueTo="0.5" /> <objectAnimator android:duration="1000" android:propertyName="alpha" android:valueFrom="1" android:valueTo="0.5" /> </set> 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455对于在XML中定义的动画,在代码中我们这样来使用:
Animator anim0 = AnimatorInflater.loadAnimator(context,R.animator.object_set); anim0.setTarget(imageView); anim0.start(); 123 123我们也可以自己写ObjectAnimator来实现属性动画:
//如果只写valueTo,则valueFrom表示当前的值 //创建了两个ObjectAnimator,两个动画同时执行 ObjectAnimator.ofFloat(imageView,"rotationX",0f,360f).setDuration(500).start(); ObjectAnimator.ofFloat(imageView,"rotationY",0f,360f).setDuration(500).start(); 1234 1234上面一个ObjectAnimator只修改一个属性,我们还可以让一个ObjectAnimator同时修改多个属性,使用PropertyValuesHolder:
//一个动画对多个属性进行修改。PropertyValuesHolder对应某一个属性 PropertyValuesHolder x = PropertyValuesHolder.ofFloat("x",0,250);//里面会生成Keyframe的集合 PropertyValuesHolder y = PropertyValuesHolder.ofFloat("y",0,250); PropertyValuesHolder sx = PropertyValuesHolder.ofFloat("scaleX",1.0f,0.5f); PropertyValuesHolder sy = PropertyValuesHolder.ofFloat("scaleY",1.0f,0.5f); PropertyValuesHolder ry = PropertyValuesHolder.ofFloat("rotationY",0,360); ObjectAnimator.ofPropertyValuesHolder(imageView,x,y,sx,sy,ry).setDuration(3000).start(); 1234567 1234567我们还可以对其中的关键帧进行设置,比如:
//一个PropertyValuesHolder对应一个Keyframe集合 Keyframe k1 = Keyframe.ofFloat(0,0);//动画开始,第二个参数不关心是哪个属性 k1.setInterpolator(new LinearInterpolator()); Keyframe k2 = Keyframe.ofFloat(0.5f,360);//动画进行到一半了,第二个参数不关心是哪个属性 k2.setInterpolator(new BounceInterpolator()); Keyframe k3 = Keyframe.ofFloat(1.0f,400);//动画完成,第二个参数不关心是哪个属性 k3.setInterpolator(new BounceInterpolator()); //这里在PropertyValuesHolder里设置需要操作的属性 PropertyValuesHolder pvh = PropertyValuesHolder.ofKeyframe("y",k1,k2,k3); ObjectAnimator.ofPropertyValuesHolder(imageView,pvh).setDuration(2000).start(); 12345678910 12345678910如果我们想操作的对象属性,没有set|get方法,那么我们需要操作时,就可以使用ValueAnimator。下面是抛物线下落的 例子(例子不是很好):
//当对象中某属性没有set/get方法时,就不能使用ObjectAnimator来操作属性动画,这时需要ValueAnimator ValueAnimator valueAnimator = ValueAnimator.ofFloat(1.0f,400f).setDuration(3000); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.setTarget(imageView); //每隔10ms就刷新 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //ValueAnimator本身不对对象属性进行修改,需要自己来处理。这里实现了抛物线下落 float value = (float) animation.getAnimatedValue(); ivPropertyAnim.setX(value); ivPropertyAnim.setY(0.08f * value * value); } }); valueAnimator.start(); 123456789101112131415 123456789101112131415Tween动画中我们可以通过applyTransformation(float interpolatedTime, Transformation t)来自定义动画,也可以通过自定义插值器,覆写getInterpolation方法来定制动画;前面我们通过addUpdateListener来定制ValueAnimator动画,这里我们通过setEvaluator来定制属性动画。
ObjectAnimator.ofFloat(ivPropertyAnim,"x",0f,360f).setDuration(5000).start(); ObjectAnimator anim1 = ObjectAnimator.ofFloat(ivPropertyAnim,"y",0f,200f).setDuration(5000); //定制属性动画 anim1.setEvaluator(new TypeEvaluator<Float>() { @Override public Float evaluate(float fraction, Float startValue, Float endValue) { System.out.println("fraction=" + fraction +",start="+startValue+",end="+ endValue); return startValue * 0.008f * fraction * (endValue - startValue) * fraction * (endValue - startValue); } }); anim1.start(); 1234567891011 1234567891011在Android API 12时,View中有一个animator()方法,返回ViewPropertyAnimator对象。
myView.animate().xBy(100).yBy(100).alpha(0.5f).rotation(180); 1 1使用比属性动画更加简单。
布局中有View的添加、删除、显示或隐藏时自身动画和View的动画。
LayoutTransition.APPEARING:View添加或出现时显示的动画LayoutTransition.CHANGE_APPEARING:当添加View导致整个布局改变时布局的动画LayoutTransition.DISAPPEARING:当View消失或隐藏时View消失的动画LayoutTransition.CHANGE_DISAPPEARING:删除或隐藏View导致布局改变时布局的动画LayoutTransition.CHANGING:当不是由于View的原因导致其它View改变时布局的动画。下面的代码在LinearLayout中动态添加一个按钮时,显示一个动画,从0缩放到正常大小的一个动画。
LayoutTransition layoutTransition = new LayoutTransition(); PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("scaleX",0.0f,1.0f); PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("scaleY",0.0f,1.0f); layoutTransition.setAnimator(LayoutTransition.APPEARING,ObjectAnimator.ofPropertyValuesHolder(btn,p1,p2)); linearLayout.setLayoutTransition(layoutTransition); linearLayout.addView(btn); 123456 123456Transition动画是个动画框架,主要用于场景的切换动画。从Android4.4引入。Android5.0中,又将Transition动画引入到Activity的切换中实现复杂的动画效果。Material Design中,推荐使用Transition动画实现动画和享元切换。已经内建了一些效果,比如Fade,Explode,Slide。Transition动画封装和抽象了属性动画。
Scene:保存ViewGroup中所有元素的状态,包括View和属性。Target:Scene里的View对象,可以在Scene中移除和或添加Target对象Transition:Scene切换时所用的动画信息TransitionSet:动画集合TransitionManager:在Scene间施加相应的Transition动画TransitionManager.beginDelayTransiton()TransitionListener:Transition的动画监听。非享元方式:
享元方式:
为了提供更加美观一致的 UI 和更好的交互效果,Android 5.0之后引入了Material Design,其中它对于动画效果有了新的要求,并也提供了6种新的动画类。 Material Design是一种风格、规范、理念和拟物和扁平设计的结合。Material Design要体现物体的三维大小,材质、轻重、弹性。
保留物体的物理特性同时,不失优雅、简约和美丽动画有利于集中注意力,并保持连续性反馈要微妙而清晰,过渡动画要高校和连贯。动画不止增加美丽效果,更加建立空间关系、功能指示、和系统意向功能。触摸反馈。提供了用户与UI交互时可视化的确认接触点。RippleDrawable类做background,涟漪效果在两种不同的状态间过渡。
物体出现或消失的动画效果。构造方法如下:
ViewAnimationUtils.createCircularReveal(View v, int centerX, int centerY, float startRadius, float endRadius)=>Animator 1 1代码中在点击事件中让一个button显示或消失过程中产生动画:
Animator animator = ViewAnimationUtils.createCircularReveal( btn, btn.getWidth() / 2, btn.getHeight() / 2, 0, btn.getWidth()); animator.setInterpolator(new LinearInterpolator()); animator.setDuration(6000); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); btn.setVisibility(View.VISIBLE); } }); animator.start(); 123456789101112131415161718 123456789101112131415161718Activity的过渡动画:进入、退出、共享元素下的进、出 进出支持这些transition效果:Explode、Slide和Fade 共享元素transition效果:changeBounds、changeClipBounds、changeTransform、changeImageTransform 用Activity.finishAfterTransition()方法,而不是Activity.finish()。
在res/transition目录下,可以添加过渡动画的xml文件:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> <explode android:duration="2000" /> </transitionSet> 或者 <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> <fade android:duration="2000" android:fadingMode="fade_in_out"/> </transitionSet> 或者 <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> <slide android:duration="2000" android:slideEdge="right" /> </transitionSet> 1234567891011 1234567891011然后在自定义主题中,设置window的进入退出共享元素下的进出动画:在res/values-v21目录下的style中:
<style name="myTheme" parent="android:Theme.Material"> <!-- 允许使用transitions --> <item name="android:windowContentTransitions">true</item> <item name="android:windowAllowEnterTransitionOverlap">true</item> <!-- 指定进入和退出transitions --> <item name="android:windowEnterTransition">@android:transition/explode</item> <item name="android:windowExitTransition">@android:transition/explode</item> <!-- 指定shared element transitions --> <item name="android:windowSharedElementEnterTransition"> @android:transition/move</item> <item name="android:windowSharedElementExitTransition"> @android:transition/slide_top</item> </style> 123456789101112131415 123456789101112131415在Activity的onCreate中设置过渡动画:
@Override protected void onCreate(Bundle savedInstanceState) { getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); getWindow().setEnterTransition(new Explode().setInterpolator(new BounceInterpolator()).setDuration(2000)); getWindow().setExitTransition(new Fade().setDuration(2000)); getWindow().setReturnTransition(new Slide().setDuration(2000)); getWindow().setReenterTransition(new Slide().setDuration(2000)); getWindow().setSharedElementReenterTransition(new ChangeTransform().setInterpolator(new BounceInterpolator()).setDuration(2000)); super.onCreate(savedInstanceState); setContentView(R.layout.first3); } 在startActivity的地方这样做: case R.id.transition://过渡动画 startActivity(new Intent(First3Effect.this, Second3Effect.class), ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); break; case R.id.sharetransition://享元模式的过渡动画 View sharedView = findViewById(R.id.p1); String transitionName = "p1"; ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(First3Effect.this, sharedView, transitionName); startActivity(new Intent(First3Effect.this, Second3Effect.class), transitionActivityOptions.toBundle()); 如果有多个共享元素 // ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, // Pair.create(view1, "agreedName1"), // Pair.create(view2, "agreedName2")); 123456789101112131415161718192021222324252627 123456789101112131415161718192021222324252627曲线运动。自定义动画时间曲线和曲线运动模式 PathInterpolator类是一个新的基于贝塞尔曲线路径的插值器 系统提供了三种基本的曲线: 1. @interpolator/fast_out_linear_in.xml 2. @interpolator/fast_out_slow_in.xml 3. @interpolator/linear_out_slow_in.xml ObjectAnimator.ofFloat(view, View.X, View.Y, path);
StateListAnimator类定义视图状态变化时的动画效果 layout:android:stateListAnimator AnimationInflater.loadStateListAnimator() View.setStateListAnimator()
VectorDrawable矢量图片伸缩不失真 AnimatedVectorDrawable矢量图动画 矢量图动画通常包括三类xml文件: 1、<vector>矢量图,在res/drawable/ 2、<animated-vector>矢量图,在res/drawable/ 3、用<objectAnimator>元素,在res/anim/