Android最新动画框架完全解析(二)——Transitions Framework(Transitions 框架)

前面一篇文章讲解了Android动画Animator,但是不知道你有没有发现,前面讲解的所有的动画都是针对某一Object来进行的,虽然我们可以对整个Layout添加动画效果,但这是先把整个layout看成一个整体,再对这个整体添加动画效果。当我们想同时对多个Object添加动画效果时又该怎么做呢?

先来看一下效果

Android最新动画框架完全解析(二)——Transitions Framework(Transitions 框架)_第1张图片

为什么要使用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 app
mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);

// Create the scenes
mAScene = 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()

@Override
public 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ㄒ)/~~

补充

参考示例

你可能感兴趣的:(框架,android,动画,transition,Scene)