Welcome 微信登录

首页 / 移动开发 / Android / 详解Android Material Design自定义动画的编写

新的动画Api,让你在UI控件里能创建触摸反馈,改变View的状态,切换activity的一系列自定义动画
具体有:
  • 响应View的touch事件的触摸反馈动画
  • 隐藏和显示View的循环展示动画
  • 两个Activity间的切换动画
  • 更自然的曲线运动的动画
  • 使用View的状态更改动画,能改变一个或多个View的属性
  • 在View的状态更改时显示状态列表动画
这些new animations Api,已内置在标准Widget中,如Button。在自定义view时也可使用这些api
201647145144001.gif (252×480)
动画在Material设计中,为用户与app交互反馈他们的动作行为和提供了视觉上的连贯性。Material主题为Buttons和Activity的过渡提供了一些默认的动画,在android5.0(api21)及以上,允许自定义这些动画:
  • Touch feedback  触摸反馈
  • Circular Reveal  循环显示
  • Activity transitions  活动过渡
  • Curved motion       曲线运动
  • View state changes  视图状态变化
  • Customize Touch Feedback  自定义触摸反馈动画
在Material设计中,触摸反馈提供了一种在用户与UI进行交互时 即时可视化的确认接触点。关于buttons默认的触摸反馈动画,使用了RippleDrawable类,用一个波纹(涟漪)效果在两种不同的状态间过渡。
在多数情况下,你需要在view的xml定义中,定义它的背景:
  • android:attr/selectableItemBackground                              有界限的波纹   
  • android:attr/selectableItemBackgroundBorderless             延伸到view之外的波纹     note:该属性为api21添加
或者,你可以用xml定义一个RippleDrawable类型的资源,并使用波纹属性。
你可以指定一个颜色给RippleDrawable对象,以改变它的默认触摸反馈颜色,使用主题的android:colorControlHighlight属性。
Use the Reveal Effect  使用展现效果
ViewAnimationUtils.createCircularReveal()方法使您能够激活一个循环显示或隐藏一个视图。
显示:

// previously invisible viewView myView = findViewById(R.id.my_view);// get the center for the clipping circleint cx = (myView.getLeft() + myView.getRight()) / 2;int cy = (myView.getTop() + myView.getBottom()) / 2;// get the final radius for the clipping circleint finalRadius = myView.getWidth();// create and start the animator for this view// (the start radius is zero)Animator anim =ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);anim.start();隐藏// previously visible viewfinal View myView = findViewById(R.id.my_view);// get the center for the clipping circleint cx = (myView.getLeft() + myView.getRight()) / 2;int cy = (myView.getTop() + myView.getBottom()) / 2;// get the initial radius for the clipping circleint initialRadius = myView.getWidth();// create the animation (the final radius is zero)Animator anim =ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);// make the view invisible when the animation is doneanim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);myView.setVisibility(View.INVISIBLE);}});// start the animationanim.start();
Customize Activity Transitions  定义Activity的过渡动画

  • 一个enter transition表示,Activity的进入场景。比如一个explode enter transition,表示Views的进入场景:飞快的从外部向屏幕中心移动。
  • 一个exit transition表示,Activity的离开场景。比如一个explode exit transition,表示Views的离开场景:从屏幕中心散开。
  • 一个share transition表示,在两个Activity间共享它们的activity transtion。比如,两个Activity有一个相同的图片,而位置和尺寸不同,使用changeImageTransform这个共享元素,能在Activity间平稳的转换和缩放图片。
android5.0(api21)及以上,支持这些效果的transition(过渡):
  • 爆炸——移动视图或从场景中心。class Explode
  • 滑行——移动视图或从一个场景的边缘。class Slide
  • 淡入淡出——添加或从场景中删除视图通过改变其透明度。 class Fade
也支持这些共享元素(都有对应的class)转换:
  • changeBounds ——View的布局的边界变化。
  • changeClipBounds——View的裁剪边界变化。
  • changeTransform——View的旋转、缩放边界变化
  • changeImageTransform——目标图像的尺寸和缩放变化。
当启用活动在你的应用程序转换,默认同时淡出淡入之间的过渡是激活进入和退出活动。
Specify custom transitions 自定义过渡动画
首先需要在定义主题的style中,使用android:windowContentTransitions属性,声明使用transitions。也可以定义使用的Transitions:

<?xml version="1.0" encoding="utf-8"?> <resources> <style name="MyTheme" parent="@android:style/Theme.Material"> <!-- enable window content transitions --> <item name="android:windowContentTransitions">true</item> <!-- specify enter and exit transitions --> <item name="android:windowEnterTransition">@android:transition/explode</item> <item name="android:windowExitTransition">@android:transition/explode</item> <!-- specify shared element transitions --> <item name="android:windowSharedElementEnterTransition">@android:transition/move</item> <item name="android:windowSharedElementExitTransition">@android:transition/slide_top</item> </style> </resources> 
注:每个transition的xml中定义的就是一组change的元素
在代码中启用transitions:

// inside your activity (if you did not enable transitions in your theme)getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);// set an exit transitiongetWindow().setExitTransition(new Explode());在代码中设置transitions的方法还有Window.setEnterTransition()Window.setExitTransition()Window.setSharedElementEnterTransition()Window.setSharedElementExitTransition()
要想尽快进行transitions过渡,可在Activity中调用Window.setAllowEnterTransitionOverlap()。
Start an activity using transitions 使用过渡启动Activity
如果你要启用transtions并设置为一个Activity的结束exit transtion,当你以如下方式启动另一个Activity时,它将被激活:

startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
当你在另一个Activity中设置了enter transtion,在其启动时,它将被激活。想要disable transitions,那么在启动另一个Activity时:

startActivity(intent,null); //传递null 的options bundle
Start an activity with a shared element  使用一个共享元素启动Acitvity
1.在主题中启用window content
2.在style中定义共享的过渡transitions
3.定义transitions的xml资源  res/transition
4.在layout中调用android:transitionName="" 设置第3步中定义的名字
5.调用 ActivityOptions.makeSceneTransitionAnimation()生成相应的ActivityOptions对象。

// get the element that receives the click eventfinal View imgContainerView = findViewById(R.id.img_container);// get the common element for the transition in this activityfinal View androidRobotView = findViewById(R.id.image_small);// define a click listenerimgContainerView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Intent intent = new Intent(this, Activity2.class);// create the transition animation - the images in the layouts// of both activities are defined with android:transitionName="robot"ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, androidRobotView, "robot");// start the new activitystartActivity(intent, options.toBundle());}});
在代码中可以用View.setTransitionName()来设置过渡动画
当你要关闭第二个Activity时,要反转过渡动画,那么可以调用Activity.finishAfterTransition()方法,而不是Activity.finish()。
Start an activity with multiple shared elements  用多共享元素启动Activity
若两个Activity拥有不只一个的共享元素,要在它们之间开始场景transition动画,在它们的layout中都要使用 android:transitionName (或在Activity中代码中调用View.setTransitionName() )来定义,并创建一个如下的 ActivityOptions 对象:

ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,Pair.create(view1, "agreedName1"),Pair.create(view2, "agreedName2"));
Use Curved Motion 使用曲线运动
在Material设计中的动画,依赖于曲线的时间插入值和空间运动模式。在android5.0(api21)及以上,可以自定义动画时间曲线和曲线运动模式。
PathInterpolator类是一个新的基于贝塞尔曲线或路径对象的插入器。这个插入器指定了一个1 x1正方形运动曲线,它使用(0,0)为锚点,(1,1)为控制点,作为构造函数的参数。你也可以定义一个path interpolator的xml资源:
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"android:controlX1="0.4"android:controlY1="0"android:controlX2="1"android:controlY2="1"/>
系统提供了三种基本的曲线,XML资源:
  • @interpolator/fast_out_linear_in.xml
  • @interpolator/fast_out_slow_in.xml
  • @interpolator/linear_out_slow_in.xml
您可以用PathInterpolator对象作Animator.setInterpolator()方法的参数。
ObjectAnimator类有新构造函数使您能够激活坐标沿着一个path同时使用两种或两种以上的属性。比如,如下的animator就使用了一个path 对象,来同时操作View的x和y属性:

ObjectAnimator mAnimator;mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);...mAnimator.start();
Animate View State Changes  视图状态改变动画
StateListAnimator类允许您定义动画运行时视图的状态变化。下面的例子演示如何在xml中定义一个StateListAnimator:

<!-- animate the translationZ property of a view when pressed --><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"><set> <objectAnimator android:propertyName="translationZ"android:duration="@android:integer/config_shortAnimTime"android:valueTo="2dp"android:valueType="floatType"/><!-- you could have other objectAnimator elements here for "x" and "y", or other properties --></set> </item> <item android:state_enabled="true"android:state_pressed="false"android:state_focused="true"><set> <objectAnimator android:propertyName="translationZ"android:duration="100"android:valueTo="0"android:valueType="floatType"/></set> </item></selector>
在上例中,为一个View添加视图状态动画,定义了一个使用selector元素的xml资源,并赋给view的android:stateListAnimator属性。如要在代码中为View指定视图状态动画,可使用AnimationInflater.loadStateListAnimator()加载xml资源,并使用View.setStateListAnimator()将其指定给View。
当你的主题继承了Material主题,按钮默认拥有了z动画。为了避免这种行为在你的按钮,设置android:stateListAnimator属性值为null。
AnimatedStateListDrawable类允许您创建图片以显示关联View的状态改变动画。一些系统的Widget,在5.0上默认使用这些动画。下面的例子显示了如何定义一个AnimatedStateListDrawable作为XML资源:

<!-- res/drawable/myanimstatedrawable.xml --><animated-selectorxmlns:android="http://schemas.android.com/apk/res/android"><!-- provide a different drawable for each state--><item android:id="@+id/pressed" android:drawable="@drawable/drawableP"android:state_pressed="true"/><item android:id="@+id/focused" android:drawable="@drawable/drawableF"android:state_focused="true"/><item android:id="@id/default"android:drawable="@drawable/drawableD"/><!-- specify a transition --><transition android:fromId="@+id/default" android:toId="@+id/pressed"><animation-list><item android:duration="15" android:drawable="@drawable/dt1"/><item android:duration="15" android:drawable="@drawable/dt2"/>...</animation-list></transition>...</animated-selector>
Animate Vector Drawables  矢量图片动画
矢量图片是可伸缩而不失真的。AnimatedVectorDrawable类让你能使一个矢量图动起来。
通常在三种xml定义动态的矢量图:
  • 使用<vector>元素的矢量图,在res/drawable/
  • 一个动态矢量图,使用<animated-vector>元素,在res/drawable/
  • 一个或多个object animator,使用<objectAnimator>元素,在res/animator/
矢量图可以定义的属性元素有<group>和<path>,<group>定义了一个<path>的集合,或者子<group>,<path>定义绘制的路径。
定义矢量图时,可以给<group>和<path>指定一个名字,示例如下:

<!-- res/drawable/vectordrawable.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android"android:height="64dp"android:width="64dp"android:viewportHeight="600"android:viewportWidth="600"><groupandroid:name="rotationGroup"android:pivotX="300.0"android:pivotY="300.0"android:rotation="45.0" ><pathandroid:name="v"android:fillColor="#000000"android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /></group></vector>
在矢量动画中,引用矢量图定义的名字:

<!-- res/drawable/animvectordrawable.xml --><animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/vectordrawable" ><targetandroid:name="rotationGroup"android:animation="@anim/rotation" /><targetandroid:name="v"android:animation="@anim/path_morph" /></animated-vector>
以下例子代表了一个 ObjectAnimator or AnimatorSet 对象:动作为旋转360度

<!-- res/anim/rotation.xml --><objectAnimatorandroid:duration="6000"android:propertyName="rotation"android:valueFrom="0"android:valueTo="360" />
下面的例子表示矢量图path从一个图形到另一个。两种渐变路径必须一致:他们必须具有相同数量的命令和相同数量的每个命令的参数:

<!-- res/anim/path_morph.xml --><set xmlns:android="http://schemas.android.com/apk/res/android"><objectAnimatorandroid:duration="3000"android:propertyName="pathData"android:valueFrom="M300,70 l 0,-70 70,70 0,0-70,70z"android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"android:valueType="pathType" /></set>