標籤雲

搜尋此網誌

2015/09/07

Animation - Transition 的子類別與自訂 Transition

Transition 實際上是使用 Property Animation 來完成
這就是說我們需要給他起始值與結束值
而若我們要繼承 Transition 類別來自訂 Transition 當然也要覆寫與此相關的 method

public class CustomTransition extends Transition {

    private static final String PROPNAME_BACKGROUND = "com.example.android.customtransition:CustomTransition:background";

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        //Transition 機制是藉 captureStartValues 此方法把 starting scene 每個 view 的屬性存在一個類別為 TransitionValues 的 Map 物件中
        //由於這是自訂的 Transition Class, 所以存我們需要的屬性就好了
        //為了怕 key 會重複,使用這樣的命名規則 package_name:transition_name:property_name
        //相對的 ending scene 裡 view 的屬性則是用 captureEndValues 存到傳進來的 TransitionValues 物件中(與 captureStartValues 的不是同一個)
        //官方範例是寫了一個 captureValues(transitionValues) 這樣的方法給 captureStartValues 跟 captureEndValues 用
        captureValues(transitionValues);
    }
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    private void captureValues(TransitionValues transitionValues) {
        View view = transitionValues.view;
        transitionValues.values.put( PROPNAME_BACKGROUND, view.getBackground());
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        //這裡會給我們之前存入的 startValues 跟 endValues, 而我們需要回傳一個 Animator 物件
        //若 view 僅存在於 starting scene 而不在 ending scene 出現, 則 endValues 會是 null, 反之亦同理
        //以下官方範例 code 是一個背景顏色的漸變轉場

        if (null == startValues || null == endValues) {
            return null;
        }

        final View view = endValues.view;
        Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND);
        Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND);

        //背景是 ColorDrawable 的才動作
        if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
            ColorDrawable startColor = (ColorDrawable) startBackground;
            ColorDrawable endColor = (ColorDrawable) endBackground;

            if (startColor.getColor() != endColor.getColor()) {
                // 這裡用了 ValueAnimator 及 ArgbEvaluator 把 #argb 背景色漸變過去
                ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), startColor.getColor(), endColor.getColor());
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        Object value = animation.getAnimatedValue();
                        if (null != value) {
                            view.setBackgroundColor((Integer) value);
                        }
                    }
                });
                return animator;
            }
        }
        // 不是 ColorDrawable 的背景就直接回傳 null 不做動畫
        return null;
    }
}
關於 Animator 可以參考之前這篇 Animation - Property Animations

2015/9/8 更新:
剛好這幾天同事在用 Transition 碰上了 ImageView 無法旋轉轉場的問題
(例如 scene1 的 ImageView 沒有旋轉, 但 scene2 的同一個 ImageView 旋轉了 90 度)
(預設的 AutoTransition 只有會做 fades, moves, 跟 resizes )
既然如此那就用自訂 Transition 來做吧

//因為 code 跟上面大同小異,除 createAnimator 外都省略
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
    if (null == startValues || null == endValues) {
        return null;
    }
    if(endValues.view instanceof ImageView){
        float startRotation = (float)startValues.values.get(PROPNAME_ROTATION);
        float endRotation = (float)endValues.values.get(PROPNAME_ROTATION);

        if(startRotation != endRotation){
            final ImageView view = (ImageView)endValues.view;
            //用 ObjectAnimator 來處理旋轉
            ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.ROTATION.getName(), startRotation, endRotation);
            animator.setInterpolator(new LinearInterpolator()); //線性
            return animator;
        }
    }
    return null;
}
在 TransitionManager.go 裡傳入了這個 CustomTrans 後
雖然可以旋轉了但位置跟大小都沒有去處理
所以要用 TransitionSet 把原本的 ChangeBounds 給加進來

TransitionSet tranSet = new TransitionSet();
tranSet.setOrdering(TransitionSet.ORDERING_TOGETHER); //同時
tranSet.addTransition(new ChangeBounds());
CustomTrans customTrans = new CustomTrans();
tranSet.addTransition(customTrans);
TransitionManager.go(mEndingScene, tranSet);
但是寫完後發現 Transition 的子類別 ChangeTransform 就有管理旋轉
用法一樣是加進 TransitionSet 裡就可以了 (或是加在 transition xml 裡面也行)
ㄜ.....剛剛那個就當作練習

順便把 Transition 家族都認識一下吧
java.lang.Object
 ↳ android.transition.Transition

    ↳ android.transition.ChangeBounds // layout bounds 相關

    ↳ android.transition.ChangeClipBounds // getClipBounds() 相關

    ↳ android.transition.ChangeImageTransform // ImageView's matrix 相關(size, shape, ImageView.ScaleType)

    ↳ android.transition.ChangeScroll // scroll 相關

    ↳ android.transition.ChangeTransform // scale and rotation 相關

    ↳ android.transition.TransitionSet //enables more complex choreography of transitions

       ↳ android.transition.AutoTransition //預設的 transition, automatically fades, moves, and resizes

    ↳ android.transition.Visibility // visibility 相關

       ↳ android.transition.Explode // moves views in or out from the edges of the scene 彈出/彈入?

       ↳ android.transition.Fade // 淡出/淡入

       ↳ android.transition.Slide // 飛出/飛入

相關資料:
Creating Custom Transitions
CustomTransition 官方範例
Reference - Transition

沒有留言: