前面一篇文章讲解了Android动画Animator,但是不知道你有没有发现,前面讲解的所有的动画都是针对某一Object来进行的,虽然我们可以对整个Layout添加动画效果,但这是先把整个layout看成一个整体,再对这个整体添加动画效果。当我们想同时对多个Object添加动画效果时又该怎么做呢?
先来看一下效果
为什么要使用Transitions:
- ViewGroup级别的动画效果
- 只需确定动画的开始和结束的状态就可以完成整个动画
- 有可以直接使用的常用动画
- 支持从资源文件(Resource)载入动画
- 生命周期中有回调函数,可以更好的控制动画效果
Scenes
一个Scene保存了一个ViewGroup中所有元素的状。同时他还拥有一个关于这个ViewGroup的父ViewGroup的引用,这个父ViewGroup称为scene root。
Transitions
关于动画的信息都存在一个Transition 对象中。通过 TransitionManager 使用Transition中动画。Transitions 框架可以在两个不同的Scene或者同一Scene的不同元素之间使用动画。
限制
- API Level 19 (Android 4.4.2)
- 使用在SurfaceView上可能会出错
SurfaceView 是在一个非UI线程上更新的,所以可能会更其他元素的动画不同步。 - 使用在TextureView上可能会出错
- 使用在继承自AdapterView的View,比如ListView上可能会出错。
- 当你对TextView 使用动画的时候,里面的文字在动画没有结束之前可能会跑到其他地方。
创建Scene
Scene mAScene;Scene mAnotherScene;// Create the scene root for the scenes in this appmSceneRoot = (ViewGroup) findViewById(R.id.scene_root);// Create the scenesmAScene = Scene.getSceneForLayout(mSceneRoot, R.layout.a_scene, this);mAnotherScene = Scene.getSceneForLayout(mSceneRoot, R.layout.another_scene, this);
创建Scene Actions
Transitions 框架允许自定义进入和退出Scene时的Action。把自定义的Action定义成Runnable对象并把他们作为参数传到Scene.setExitAction() 或者 Scene.setEnterAction() 中。系统会在进入和退出的时候调用这两个方法。
创建Transition
从resource文件中创建Transition
步骤如下:
1 . 在项目中添加res/transition/目录
2 . 在目录中新建XML文件
res/transition/fade_transition.xml
<fade xmlns:android="http://schemas.android.com/apk/res/android" />
3 . 在Activity中加载
Transition mFadeTransition = TransitionInflater.from(this). inflateTransition(R.transition.fade_transition);
在代码中动态添加Transition
Transition mFadeTransition = new Fade();
调用Transition
TransitionManager.go(mEndingScene, mFadeTransition);
通过这一条语句,scene root中的View就会从初始状态根据Transition 变为结束状态。
对特定的View使用Transition
由于Transition框架并不是对所有的对象都适用(比如ListView ),所以有时我们需要指定使用Transition的对象。
每一个使用Transition的对象叫做一个target,当然这个对象需要在Scene中。可以通过在开始transition前调用 removeTarget() 方法去除不支持的对象或者调用addTarget()来添加对象。
使用transitionSet
transitionSet类似Animation中的Set,是一个动画集合。定义在XML中,如下:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:transitionOrdering="sequential"> <fade android:fadingMode="fade_out" /> <changeBounds /> <fade android:fadingMode="fade_in" /></transitionSet>
在Activity中调用TransitionInflater.from() 来加载TransitionSet。TransitionSet继承自Transition,能用Transition的地方都可以使用TransitionSet。
自定义Transitions
继承Transition 类
public class CustomTransition extends Transition { @Override public void captureStartValues(TransitionValues values) {} @Override public void captureEndValues(TransitionValues values) {} @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {}}
重写captureStartValues()
框架会对开始Scene中的每一个对象调用captureStartValues()方法,方法的参数是TransitionValues 对象,这个对象包含对应这个View的一个引用和一个Map实例,这个Map实例用来保存你需要的属性值,为了保证属性值的Key不与其他的TransitionValues 的Key 冲突,推荐使用如下的命名规则。
package_name:transition_name:property_name
下面是一个重写 captureStartValues() 的例子:
public class CustomTransition extends Transition { // Define a key for storing a property value in // TransitionValues.values with the syntax // package_name:transition_class:property_name to avoid collisions private static final String PROPNAME_BACKGROUND = "com.example.android.customtransition:CustomTransition:background"; @Override public void captureStartValues(TransitionValues transitionValues) { // Call the convenience method captureValues captureValues(transitionValues); } // For the view in transitionValues.view, get the values you // want and put them in transitionValues.values private void captureValues(TransitionValues transitionValues) { // Get a reference to the view View view = transitionValues.view; // Store its background property in the values map transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground()); } ...}
captureEndValues()
@Overridepublic void captureEndValues(TransitionValues transitionValues) { captureValues(transitionValues);}
与captureStartValues()类似,把结束的值放入TransitionValues 的Map对象中,captureEndValues()中的Map对象与captureStartValues()中的Map不是同一个对象,put()的时候请放心~
createAnimator()
创建一个Animator用来负责从初始状态到结束状态的动画效果,并把这个Animator返回。下面是一个改变背景颜色的例子。
// Create an animation for each target that is in both the starting and ending Scene. For each // pair of targets, if their background property value is a color (rather than a graphic), // create a ValueAnimator based on an ArgbEvaluator that interpolates between the starting and // ending color. Also create an update listener that sets the View background color for each // animation frame @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { // This transition can only be applied to views that are on both starting and ending scenes. if (null == startValues || null == endValues) { return null; } // Store a convenient reference to the target. Both the starting and ending layout have the // same target. final View view = endValues.view; // Store the object containing the background property for both the starting and ending // layouts. Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND); Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND); // This transition changes background colors for a target. It doesn't animate any other // background changes. If the property isn't a ColorDrawable, ignore the target. if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) { ColorDrawable startColor = (ColorDrawable) startBackground; ColorDrawable endColor = (ColorDrawable) endBackground; // If the background color for the target in the starting and ending layouts is // different, create an animation. if (startColor.getColor() != endColor.getColor()) { // Create a new Animator object to apply to the targets as the transitions framework // changes from the starting to the ending layout. Use the class ValueAnimator, // which provides a timing pulse to change property values provided to it. The // animation runs on the UI thread. The Evaluator controls what type of // interpolation is done. In this case, an ArgbEvaluator interpolates between two // #argb values, which are specified as the 2nd and 3rd input arguments. ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), startColor.getColor(), endColor.getColor()); // Add an update listener to the Animator object. animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Object value = animation.getAnimatedValue(); // Each time the ValueAnimator produces a new frame in the animation, change // the background color of the target. Ensure that the value isn't null. if (null != value) { view.setBackgroundColor((Integer) value); } } }); // Return the Animator object to the transitions framework. As the framework changes // between the starting and ending layouts, it applies the animation you've created. return animator; } } // For non-ColorDrawable backgrounds, we just return null, and no animation will take place. return null; }
总结
关于Android动画的所有基本知识到此就讲完了,Animation主要为了实现单个对象的动画效果,Transitions 框架可以同时实现多个对象的动画效果。在实际项目中还需要根据具体需求选择。基本知识虽然讲完了,但是如何实现优美的效果还是很考验美术功底的,比如说博主就是一个典型的失败例子/(ㄒoㄒ)/~~