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

相關文章
相關標籤/搜索