详解Android Material Design自定义动画的编写
新的动画api,让你在ui控件里能创建触摸反馈,改变view的状态,切换activity的一系列自定义动画
具体有:
- 响应view的touch事件的触摸反馈动画
- 隐藏和显示view的循环展示动画
- 两个activity间的切换动画
- 更自然的曲线运动的动画
- 使用view的状态更改动画,能改变一个或多个view的属性
- 在view的状态更改时显示状态列表动画
这些new animations api,已内置在标准widget中,如button。在自定义view时也可使用这些api
动画在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 view view myview = findviewbyid(r.id.my_view); // get the center for the clipping circle int cx = (myview.getleft() + myview.getright()) / 2; int cy = (myview.gettop() + myview.getbottom()) / 2; // get the final radius for the clipping circle int 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 view final view myview = findviewbyid(r.id.my_view); // get the center for the clipping circle int cx = (myview.getleft() + myview.getright()) / 2; int cy = (myview.gettop() + myview.getbottom()) / 2; // get the initial radius for the clipping circle int 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 done anim.addlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { super.onanimationend(animation); myview.setvisibility(view.invisible); } }); // start the animation anim.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 transition getwindow().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 event final view imgcontainerview = findviewbyid(r.id.img_container); // get the common element for the transition in this activity final view androidrobotview = findviewbyid(r.id.image_small); // define a click listener imgcontainerview.setonclicklistener(new view.onclicklistener() { @override public 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 activity startactivity(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-selector xmlns: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"> <group android:name="rotationgroup" android:pivotx="300.0" android:pivoty="300.0" android:rotation="45.0" > <path android: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" > <target android:name="rotationgroup" android:animation="@anim/rotation" /> <target android:name="v" android:animation="@anim/path_morph" /> </animated-vector>
以下例子代表了一个 objectanimator or animatorset 对象:动作为旋转360度
<!-- res/anim/rotation.xml --> <objectanimator android: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"> <objectanimator android: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>