Android转场动画深度解析(1)

Android5.0以后新增了不少好看的转场动画,相比于之前的overridePendingTransition()丰富了不少,特别新增了共享元素跳转的方式。本篇文章介绍转场动画框架的基本概念,并着手本身实现转场动画。android

Scene(场景)

Scene保存了一个布局文件。咱们能够经过如下方式生成一个Scene:
public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context)
这个方法时静态的,传入一个根布局ViewGroup(做为显示场景的容器),一个layoutId(场景的显示内容),最后传入当前上下文。
源码很短,咱们一块儿来看一下:bash

public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context) {
        SparseArray<Scene> scenes = (SparseArray<Scene>) sceneRoot.getTag(
                com.android.internal.R.id.scene_layoutid_cache);
        if (scenes == null) {
            scenes = new SparseArray<Scene>();
            sceneRoot.setTagInternal(com.android.internal.R.id.scene_layoutid_cache, scenes);
        }
        Scene scene = scenes.get(layoutId);
        if (scene != null) {
            return scene;
        } else {
            scene = new Scene(sceneRoot, layoutId, context);
            scenes.put(layoutId, scene);
            return scene;
        }
    }复制代码
  • 根据一个固定的Tag取得保存依附于这个ViewGroup的scene集合SparseArray<Scene> scenes,若是是空就先new一个。
  • 以要显示场景的layoutId为Key,先尝试获取这个场景,若是已经有这个layoutId对应的场景就直接返回,没有就先调用构造方法生成一个再放入进去,而后返回。
  • 一个scene只能对应一个布局,scene只是简单保存了sceneRoot, layoutId, context的值,并无经过layoutId来分析处理里面的View信息(也没有必要)
  • 能够经过setEnterAction(Runnable action),setExitAction(Runnable action),在场景被加载和移除时回调,作相应的操做。

Transition(变换)

上面的介绍scene将一个或多个布局和一个加载这些布局的根布局创建起关系。真正的动画是由Transition实现的。
因此大体的流程是:框架

//为Scene建立scene root  
mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);  
//建立 scenes  
Scene mAScene = Scene.getSceneForLayout(mSceneRoot, R.layout.a_scene, this);  
Scene mAnotherScene =  Scene.getSceneForLayout(mSceneRoot, R.layout.another_scene, this);
  //代码中建立Transition
Transition mFadeTransition = new Fade();  
//用TransitionManager负责场景变换
TransitionManager.go(mEndingScene, mFadeTransition);复制代码

自定义Transition

Transition是个抽象类,必需要实现如下方法:ide

  • public abstract void captureStartValues(TransitionValues transitionValues);捕获当前场景的视图,这里会对视图树中全部的View调用,有几个View就会调用几回。
  • public abstract void captureEndValues(TransitionValues transitionValues);捕获目标场景的视图,这里会对视图树中全部的View调用,有几个View就会调用几回。
  • public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,TransitionValues endValues)(不实现方法这个就没动画效果)
    从命名就能够看到captureStartValuescaptureStartValues分别用来捕获当前场景和目标场景。

TransitionValues有三个重要属性,对理解Transition框架的机制有很大帮助。布局

  • View view:就是一个场景的一个View,在里面拿到View,咱们能够从里面获得这个View咱们所须要的属性。
  • Map<String, Object> values:默认为空,咱们拿到属性后须要放到里面,若是这个Transition须要改变多个属性,就能够放屡次进去。
  • ArrayList<Transition> targetedTransitions:默认为空,用来记录这个View执行了哪些Transition,咱们能够在对这个View执行Transition的时候,把这个Transition存进去。

createAnimator方法就是Transition真正的实现方法了,返回一个属性动画。动画

好了实战开始,咱们就实现一个Transition来实现直角移动:ui

public class ChangeRect extends Transition {

    private static final String PROPNAME_BER =
            "changeposition:Rect";

    // 开始的状态,这里会对视图树中全部的View调用,这里咱们能够记录一下View的咱们感兴趣的状态,好比这里:position
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);

    }

    // 结束也会对全部的View进行调用
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    private void captureValues(TransitionValues transitionValues) {
        float[] location = new float[2];
        location[0] = transitionValues.view.getX();
        location[1] = transitionValues.view.getY();
        transitionValues.values.put(PROPNAME_BER, location);
    }

    //新建动画
    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        if (null == startValues || null == endValues) {
            return null;
        }
        final View view = endValues.view;
        float[] startPosition = (float[]) startValues.values.get(PROPNAME_BER);
        float[] endPosition = (float[]) endValues.values.get(PROPNAME_BER);

        if (startPosition[0] != endPosition[0] || startPosition[1] != endPosition[1]) {
            Path path=new Path();
            path.moveTo(startPosition[0],startPosition[1]);
            path.lineTo(endPosition[0],startPosition[1]);
            path.lineTo(endPosition[0],endPosition[1]);
            ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
            animator.setDuration(getDuration());
            animator.start();

            return animator;
        }
        return null;
    }
}复制代码

总结

简述下Transition框架的执行机制,咱们定义了两个Scene,,当咱们经过 TransitionManager.go( scene , transition),从Scene跳转到目标Scene的时候,会去取得scene对应布局,遍历布局中的每个View(包括根布局和容器View),获取咱们须要的属性。经过View的Id咱们创建起两个布局中View的对应关系,因此最终只会在目标场景执行原场景有相同Id的View的动画(知足startValues != null && endValues!= null)。
这篇文章只是简单解析了转场动画的原理,详细的两个页面的跳转将会的下一篇展开。this

相关文章
相关标签/搜索