Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義 Android 轉場動畫

自定義 Android 轉場動畫

編輯:關於Android編程

在 Android 5.0上 google 官方給我提供了不少好看方便使用的轉場動畫

原生提供的普通轉場動畫
- fade 漸隱漸現
- slid 各元素先後滑動進入
- Explode 分裂成連個部分以前進入

分享元素的轉場動畫
- changeBound 這個是最長使用的 改變View 大小和位置
- changeClipBounds 改變 Clip 邊界的大小
- changeImageTransform 改變ImageView 的大小 和 martix
- ChangeTransform 改變普通的 View 一些Scalex 值
- ChangeScroll 改變滑動位置

以上都是原生的. 但是面對一些復雜的轉場動畫,google 提供的這幾個還是不夠, 很多時候都需要自己定義轉場動畫.
例如下轉場動畫, 使用原生的這些動畫很難實現:

這裡寫圖片描述

上面那個轉場動畫主要分為兩部分:

頂部 title bar 和底部輸入框的 進入 返回動畫 第一個頁面的評論圓球通過揭露效果顯示到第二頁面的內容詳情頁面

第一個問題, 頂部 title bar 和頂部輸入框進入,返回動畫

一開始我想使用 官方元素的slid或Explode來實現, 但是基本都難以實現
後面只能自己寫一個轉場動畫,轉場動畫也簡單.
只需要繼承 VisibilityTransition 其中 Visibility是繼承自Transition

如果轉場動畫只是某個 View 出現或消失, 那麼可以考慮繼承 Visibility
如果是累 ShareElem這樣的轉場動畫, 那麼就需要繼承 Transition

回到重點, 我們需要實現 , 第一個問題:

頂部 title bar 和底部輸入框的 進入 返回動畫

繼承Visibility 有4個方法需要我們重寫:
1. public void captureStartValues(TransitionValues transitionValues) 這裡保存計算動畫初始狀態的一個屬性值
2. public void captureEndValues(TransitionValues transitionValues) 這裡保存計算動畫結束狀態的一個屬性值
3. public Animator onAppear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)
如果是進入動畫 即顯示某個 View 則會執行這個方法
4. public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)
如果是退出 , 即不在顯示某個 VIew 則會執行這個方法

我們可以看到頂部 title bar 和 底部輸入框 輸入框的動畫其實非常簡單, 僅僅只有TranslationY 這一個動畫.
我們只需要在captureStartValues,captureEndValues兩個方法中分別計算和保存開始和解釋位置需要的位移
然後在 onAppearonDisappear 方法創建動畫即可

具體請查看代碼:

public class CommentEnterTransition extends Visibility {
    private static final String TAG = "CommentEnterTransition";

    private static final String PROPNAME_BOTTOM_BOX_TRANSITION_Y = "custom_bottom_box_enter_transition:change_transY:transitionY";
    private static final String PROPNAME_TOP_BAR_TRANSITION_Y = "custom_top_bar_transition:change_transY:transitionY";

    private View mBottomView;
    private View mTopBarView;
    private Context mContext;

    public CommentEnterTransition(Context context, View topBarView, View bottomView) {
        mBottomView = bottomView;
        mTopBarView = topBarView;
        mContext = context;
    }


    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        super.captureStartValues(transitionValues);
        mBottomView.measure(0, 0);
        int transY = mBottomView.getMeasuredHeight();

        // 保存 計算初始值
        transitionValues.values.put(PROPNAME_BOTTOM_BOX_TRANSITION_Y, transY);
        transitionValues.values.put(PROPNAME_TOP_BAR_TRANSITION_Y, -mContext.getResources().getDimensionPixelOffset(R.dimen.top_bar_height));
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        super.captureEndValues(transitionValues);

        // 保存計算結束值
        transitionValues.values.put(PROPNAME_BOTTOM_BOX_TRANSITION_Y, 0);
        transitionValues.values.put(PROPNAME_TOP_BAR_TRANSITION_Y, 0);
    }


    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        return super.createAnimator(sceneRoot, startValues, endValues);

    }

    @Override
    public Animator onAppear(ViewGroup sceneRoot, final View view,
                             TransitionValues startValues, TransitionValues endValues) {

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

        // 這裡去除 之前 存儲的 初始值 和 結束值, 然後執行東湖
        if (view == mBottomView) {
            int startTransY = (int) startValues.values.get(PROPNAME_BOTTOM_BOX_TRANSITION_Y);
            int endTransY = (int) endValues.values.get(PROPNAME_BOTTOM_BOX_TRANSITION_Y);

            if (startTransY != endTransY) {
                ValueAnimator animator = ValueAnimator.ofInt(startTransY, endTransY);
                // 注意這裡不能使用 屬性動畫, 使用 ValueAnimator 然後在更新 View 的對應屬性
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        Object value = animation.getAnimatedValue();
                        if (null != value) {
                            view.setTranslationY((Integer) value);
                        }
                    }
                });
                return animator;
            }
        } else if (view == mTopBarView) {

            int startTransY = (int) startValues.values.get(PROPNAME_TOP_BAR_TRANSITION_Y);
            int endTransY = (int) endValues.values.get(PROPNAME_TOP_BAR_TRANSITION_Y);

            if (startTransY != endTransY) {
                ValueAnimator animator = ValueAnimator.ofInt(startTransY, endTransY);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        Object value = animation.getAnimatedValue();
                        if (null != value) {
                            view.setTranslationY((Integer) value);
                        }
                    }
                });
                return animator;
            }
        }
        return null;
    }

    @Override
    public Animator onDisappear(ViewGroup sceneRoot, final View view,
                                TransitionValues startValues, TransitionValues endValues) {
        if (null == startValues || null == endValues) {
            return null;
        }

        // 這裡執行 返回動畫,  這裡金 初始值 和技術值 對調了,這樣動畫, 就就和原來動畫想反了
        if (view == mBottomView) {
            int startTransY = (int) endValues.values.get(PROPNAME_BOTTOM_BOX_TRANSITION_Y);
            int endTransY = (int) startValues.values.get(PROPNAME_BOTTOM_BOX_TRANSITION_Y);

            if (startTransY != endTransY) {
                ValueAnimator animator = ValueAnimator.ofInt(startTransY, endTransY);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        Object value = animation.getAnimatedValue();
                        if (null != value) {
                            view.setTranslationY((Integer) value);
                        }
                    }
                });
                return animator;
            }
        } else if (view == mTopBarView) {
            int startTransY = (int) endValues.values.get(PROPNAME_TOP_BAR_TRANSITION_Y);
            int endTransY = (int) startValues.values.get(PROPNAME_TOP_BAR_TRANSITION_Y);

            if (startTransY != endTransY) {
                ValueAnimator animator = ValueAnimator.ofInt(startTransY, endTransY);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        Object value = animation.getAnimatedValue();
                        if (null != value) {
                            view.setTranslationY((Integer) value);
                        }
                    }
                });
                return animator;
            }
        }
        return null;
    }
}

最後把自定義的轉場動畫設置上即可:

getWindow().setEnterTransition(new CommentEnterTransition(this, mTitleBarTxt, mBottomSendBar));

再看看效果:

\

第二個問題, 分享元素的揭露展開以及收縮動畫

首先一點可以明確指示一個分享元素的轉場動畫,
一開始想用原始的 ChangeBounds 來簡單實現但是有一個問題就是從圓形變成矩形的這個過程太過生硬
後來沒辦法只能自己自動轉存動畫.

我們在來分析看看上面的那個轉場動畫
1. 揭露效果: 從第一個頁面的圓球開始, 圓球慢慢的方法, 直到整個動畫結束
2. 看似 View 是在隨著動畫的過程慢慢放大
3. 似乎還有曲線位移的動畫?

其實如果真的自己寫過轉場動畫的話, 第二個可以排除了, 分享元素的轉場動畫一開始 view 就已經變成第二個頁面中的View 了, 所以 View 沒有放大過程
ok 我們首先來解決第一個問題, 揭露動畫. 這個是 Android 5.0 以後原生提供的一個動畫使用起來特別簡單:

ViewAnimationUtils.createCircularReveal(view, centerX, centerY,startRadius, endRadius);

其中
centerX, centerY是揭露動畫圓心的位置;
startRadius, endRadius則是開始時圓球的半徑 和結束時圓球的半徑

那麼加來我們就需要 繼承 Transition 來實現揭露動畫了.
繼承 Transition 需要重寫以下 3個方法:
1. public void captureStartValues(TransitionValues transitionValues) 這裡能夠獲取到 上一個頁面的對應的 View 一些屬性值
2. public void captureEndValues(TransitionValues transitionValues) 這裡能夠獲取到 即將要打開的對應的頁面的對應 View 的一些屬性值
3. public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) 這裡創建對應的轉場動畫

ok, 那麼為了實現 上面的揭露動畫, 我們需要一開始獲取的值有:
1. 上一個頁面的圓形 View 的寬度, 作為揭露動畫圓形的最開始的半徑
2. 即將要打開的頁面的 對應 View 的對角線的長度, 作為揭露動畫圓形的最終半徑
3. 獲取揭露動畫的圓心位置, 這裡我們去 View 的中間位置

ok 那麼就可實現一個簡單的轉場揭露動畫了, 先看看代碼, 然後再看看效果
代碼:

public class ShareElemEnterRevealTransition extends Transition {
    private static final String TAG = "ShareElemEnterRevealTransition";

    private static final String PROPNAME_RADIUS = "custom_reveal:change_radius:radius";

    private boolean hasAnim = false;

    private View animView;

    public ShareElemEnterRevealTransition(View animView) {
        this.animView = animView;
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        transitionValues.values.put(PROPNAME_RADIUS, transitionValues.view.getWidth() / 2);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        View view = transitionValues.view;
        float widthSquared = view.getWidth() * view.getWidth();
        float heightSquared = view.getHeight() * view.getHeight();
        int radius = (int) Math.sqrt(widthSquared + heightSquared) / 2;
        transitionValues.values.put(PROPNAME_RADIUS, radius);
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {

        if (null == startValues || null == endValues) {
            return null;
        }
        final View view = endValues.view;
        int startRadius = (int) startValues.values.get(PROPNAME_RADIUS);
        int endRadius = (int) endValues.values.get(PROPNAME_RADIUS);

        if (view == animView) {
            Animator reveal = createAnimator(view, startRadius, endRadius);
            hasAnim = true;
            return reveal;
        }

        return null;
    }

    private Animator createAnimator(View view, float startRadius, float endRadius) {
        int centerX = view.getWidth() / 2;
        int centerY = view.getHeight() / 2;
        Animator reveal = ViewAnimationUtils.createCircularReveal(view, centerX, centerY,
                startRadius, endRadius);
        return new NoPauseAnimator(reveal);
    }
}

效果:

這裡寫圖片描述

貌似差距有點大,沒有顏色的漸變, 同時貌似接通動畫的圓形是在第二頁面 View 的圓心, 沒有移動的感覺
ok , 接下來需要處理的有:
1. 移動 View
2. 背景顏色漸變

通過上面的代碼示例, 做這兩件事情應該不復雜, 下面直接上代碼:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrjEseTR1cmrOjwvcD4NCjxwcmUgY2xhc3M9"brush:java;"> public class ChangeColor extends Transition { private static final String TAG = "ChangeColor"; private static final String PROPNAME_BACKGROUND = "customtransition:change_color:backgroundcolor"; int mStartColor; int mEndColor; public ChangeColor(int startColor, int endColor) { this.mStartColor = startColor; this.mEndColor = endColor; } private void captureValues(TransitionValues values) { values.values.put(PROPNAME_BACKGROUND, values.view.getBackground()); } @Override public void captureStartValues(TransitionValues transitionValues) { captureValues(transitionValues); transitionValues.values.put(PROPNAME_BACKGROUND, mStartColor); } @Override public void captureEndValues(TransitionValues transitionValues) { transitionValues.values.put(PROPNAME_BACKGROUND, mEndColor); } @Override public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { if (null == startValues || null == endValues) { return null; } final View view = endValues.view; int startColor = (int) startValues.values.get(PROPNAME_BACKGROUND); int endColor = (int) endValues.values.get(PROPNAME_BACKGROUND); if (startColor != endColor) { ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), startColor, endColor); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Object value = animation.getAnimatedValue(); if (null != value) { view.setBackgroundColor((Integer) value); } } }); return animator; } return null; } }

改變位置(曲線運動):

public class ChangePosition extends Transition {
    private static final String TAG = "ChangePosition";

    private static final String PROPNAME_POSITION = "custom_position:change_position:position";


    public ChangePosition() {
        // 這裡通過曲線的方式 來改變位置
        setPathMotion(new PathMotion() {
            @Override
            public Path getPath(float startX, float startY, float endX, float endY) {
                Path path = new Path();
                path.moveTo(startX, startY);

                float controlPointX = (startX + endX) / 3;
                float controlPointY = (startY + endY) / 2;

                // 這裡是一條貝塞爾曲線的路基, (controlPointX, controlPointY) 表示控制點
                path.quadTo(controlPointX, controlPointY, endX, endY);
                return path;
            }
        });
    }

    private void captureValues(TransitionValues values) {
        values.values.put(PROPNAME_POSITION, values.view.getBackground());

        Rect rect = new Rect();
        values.view.getGlobalVisibleRect(rect);
        values.values.put(PROPNAME_POSITION, rect);
    }


    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot,
                                   TransitionValues startValues, TransitionValues endValues) {
        if (null == startValues || null == endValues) {
            return null;
        }

        if (startValues.view.getId() > 0) {
            Rect startRect = (Rect) startValues.values.get(PROPNAME_POSITION);
            Rect endRect = (Rect) endValues.values.get(PROPNAME_POSITION);

            final View view = endValues.view;

            Path changePosPath = getPathMotion().getPath(startRect.centerX(), startRect.centerY(), endRect.centerX(), endRect.centerY());

            int radius = startRect.centerY() - endRect.centerY();

            ObjectAnimator objectAnimator = ObjectAnimator.ofObject(view, new PropPosition(PointF.class, "position", new PointF(endRect.centerX(), endRect.centerY())), null, changePosPath);
            objectAnimator.setInterpolator(new FastOutSlowInInterpolator());

            return objectAnimator;
        }
        return null;

    }

    static class PropPosition extends Property {

        public PropPosition(Class type, String name) {
            super(type, name);
        }

        public PropPosition(Class type, String name, PointF startPos) {
            super(type, name);
            this.startPos = startPos;
        }

        PointF startPos;

        @Override
        public void set(View view, PointF topLeft) {

            int x = Math.round(topLeft.x);
            int y = Math.round(topLeft.y);

            int startX = Math.round(startPos.x);
            int startY = Math.round(startPos.y);

            int transY = y - startY;
            int transX = x - startX;

            // 這裡控制 View 移動
            view.setTranslationX(transX);
            view.setTranslationY(transY);
        }

        @Override
        public PointF get(View object) {
            return null;
        }
    }

}

上面改變位置的 使用 Path 動畫, 使得 View 能夠以貝塞爾曲線的方式進行位移

ok 上面基本上就把 enter 的動畫處理完了, 但是返回還是有點問題.
看下圖:
\

返回的時候 View 大小已經變成了後面個頁面 View 的大小了, 然後由於大小的限制揭露動畫基本也看不出效果.
所以分享元素的返回動畫我們也要做一些細微的調整.

關於改變 View大小的這個問題, 我看了下 ChangeBounds 的源碼, 然後發現, 他們是通過調用 View一個隱藏方法:

/**
 * Same as setFrame, but public and hidden. For use in {@link android.transition.ChangeBounds}.
 * @hide
 */
public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
    setFrame(left, top, right, bottom);
}

後面發現有適配問題, 這個方法只在5.1或以上才有, 在5.0 上面沒有, 然後又看了下 5.0 的ChangeBounds 源碼, 發現在 5.0 上改變 View 大小是通過以下方式實現的:

view.setLeft(left);
view.setRight(right);
view.setTop(top);
view.setBottom(bottom);

額 其實 5.0 的這個 set 方法在 5.1或以上也是可以使用的.
所以改變 View 大小這個, 可以選擇 5.1 以上用反射 調用 setLeftTopRightBottom 方法, 也可以選擇 都直接使用set 方法

下面貼上返回動畫一些代碼:
改變位置:

public class ShareElemReturnChangePosition extends Transition {
    private static final String TAG = "ShareElemReturnChangePosition";

    private static final String PROPNAME_POSITION = "custom_position:change_position:position";


    public ShareElemReturnChangePosition() {
        setPathMotion(new PathMotion() {
            @Override
            public Path getPath(float startX, float startY, float endX, float endY) {
                Path path = new Path();
                path.moveTo(startX, startY);

                float controlPointX = (startX + endX) / 3;
                float controlPointY = (startY + endY) / 2;

                path.quadTo(controlPointX, controlPointY, endX, endY);
                return path;
            }
        });
    }

    private void captureValues(TransitionValues values) {
        values.values.put(PROPNAME_POSITION, values.view.getBackground());

        Rect rect = new Rect();
        values.view.getGlobalVisibleRect(rect);
        values.values.put(PROPNAME_POSITION, rect);
    }


    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot,
                                   TransitionValues startValues, TransitionValues endValues) {
        if (null == startValues || null == endValues) {
            return null;
        }

        if (startValues.view.getId() > 0) {
            Rect startRect = (Rect) startValues.values.get(PROPNAME_POSITION);
            Rect endRect = (Rect) endValues.values.get(PROPNAME_POSITION);

            final View view = endValues.view;


            Rect rect = new Rect();
            view.getGlobalVisibleRect(rect);

            Path changePosPath = getPathMotion().getPath(startRect.centerX(), startRect.centerY(), endRect.centerX(), endRect.centerY() - endRect.height() / 2);

            ObjectAnimator objectAnimator = ObjectAnimator.ofObject(view, new PropPosition(PointF.class, "position", new PointF(startRect.centerX(), startRect.centerY())), null, changePosPath);
            objectAnimator.setInterpolator(new FastOutSlowInInterpolator());

            return objectAnimator;
        }
        return null;

    }

    static class PropPosition extends Property {

        public PropPosition(Class type, String name) {
            super(type, name);
        }

        public PropPosition(Class type, String name, PointF startPos) {
            super(type, name);
            this.startPos = startPos;
        }

        PointF startPos;

        @Override
        public void set(View view, PointF topLeft) {

            int x = Math.round(topLeft.x);
            int y = Math.round(topLeft.y);

            int startX = Math.round(startPos.x);
            int startY = Math.round(startPos.y);

            int transY = y - startY;
            int transX = x - startX;

            Rect rect = new Rect();
            view.getGlobalVisibleRect(rect);

            view.setTranslationX(transX);
            view.setTranslationY(transY);
        }

        @Override
        public PointF get(View object) {
            return null;
        }
    }
}

揭露動畫:

public class ShareElemReturnRevealTransition extends Transition {
    private static final String TAG = "ShareElemReturnRevealTransition";

    private static final String PROPNAME_BACKGROUND = "custom_reveal:change_radius:radius";

    private boolean hasAnim = false;

    private View animView;

    private Rect startRect;
    private Rect endRect;


    public ShareElemReturnRevealTransition(View animView) {
        this.animView = animView;
        startRect = new Rect();
        endRect = new Rect();
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        View view = transitionValues.view;
        float widthSquared = view.getWidth() * view.getWidth();
        float heightSquared = view.getHeight() * view.getHeight();
        int radius = (int) Math.sqrt(widthSquared + heightSquared) / 2;

        transitionValues.values.put(PROPNAME_BACKGROUND, radius);
        transitionValues.view.getGlobalVisibleRect(startRect);

    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        transitionValues.view.getLocalVisibleRect(endRect);

        transitionValues.values.put(PROPNAME_BACKGROUND, transitionValues.view.getWidth() / 2);
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues) {

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

        final View view = endValues.view;
        int startRadius = (int) startValues.values.get(PROPNAME_BACKGROUND);
        int endRadius = (int) endValues.values.get(PROPNAME_BACKGROUND);

        // 在執行返回動畫的時候,  View 默認的被控制 為 前一個頁面的 ShareElem 的打消了
        // 所以這裡 需要改變的 View 大小 才能 正常的使用揭露動畫
        // 反射調用
        relfectInvoke(view,
                startRect.left,
                startRect.top,
                startRect.right,
                startRect.bottom
        );

        Animator reveal = createAnimator(view, startRadius, endRadius);

        // 在動畫的最後 被我們放大後的 View 會閃一些  這裡可以有防止那種情況發生
        reveal.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                view.setClipBounds(new Rect(0, 0, 1, 1));
                view.setVisibility(View.GONE);
            }
        });
        return reveal;

    }

    private Animator createAnimator(View view, float startRadius, float endRadius) {
        int centerX = view.getWidth() / 2;
        int centerY = view.getHeight() / 2;

        Animator reveal = ViewAnimationUtils.createCircularReveal(view, centerX, centerY,
                startRadius, endRadius);
        return new ShareElemEnterRevealTransition.NoPauseAnimator(reveal);
    }

    // setLeftTopRightBottom 需要反射執行, 該方法能夠控制 View 的大小以及位置 在 ChangeBounds 類中有調用
    private void relfectInvoke(View view, int left, int top, int right, int bottom) {

        Class clazz = view.getClass();
        try {
            Method m1 = clazz.getMethod("setLeftTopRightBottom", new Class[]{int.class, int.class, int.class, int.class});
            m1.invoke(view, left, top, right, bottom);
        } catch (Exception e) {
            e.printStackTrace();

            // 5.0版本  沒有 setLeftTopRightBottom 這個方法  使用一下方法 ,額 其實 5.0 以上也可以用這些方法?
            view.setLeft(left);
            view.setRight(right);
            view.setTop(top);
            view.setBottom(bottom);
        }

    }

}

ok 下面貼上 Activity 中如何使用這些動畫:

public class CommentActivity extends AppCompatActivity {

    private static final String TAG = "CommentActivity";


    View mBottomSendBar;
    View mTitleBarTxt;
    View mCommentBox;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_coment);
        mCommentBox = findViewById(R.id.comment_box);
        mTitleBarTxt = findViewById(R.id.txt_title_bar);
        mBottomSendBar = findViewById(R.id.bottom_send_bar);

        setTransition();

    }

    private void setTransition() {
        // 頂部 title 和底部輸入框的進入動畫
        getWindow().setEnterTransition(new CommentEnterTransition(this, mTitleBarTxt, mBottomSendBar));

        getWindow().setSharedElementEnterTransition(buildShareElemEnterSet());
        getWindow().setSharedElementReturnTransition(buildShareElemReturnSet());

    }

    /**
     * 分享 元素 進入動畫
     * @return
     */
    private TransitionSet buildShareElemEnterSet() {
        TransitionSet transitionSet = new TransitionSet();

        Transition changePos = new ChangePosition();
        changePos.setDuration(300);
        changePos.addTarget(R.id.comment_box);
        transitionSet.addTransition(changePos);

        Transition revealTransition = new ShareElemEnterRevealTransition(mCommentBox);
        transitionSet.addTransition(revealTransition);
        revealTransition.addTarget(R.id.comment_box);
        revealTransition.setInterpolator(new FastOutSlowInInterpolator());
        revealTransition.setDuration(300);

        ChangeColor changeColor = new ChangeColor(getResources().getColor(R.color.black_85_alpha), getResources().getColor(R.color.white));
        changeColor.addTarget(R.id.comment_box);
        changeColor.setDuration(350);

        transitionSet.addTransition(changeColor);

        transitionSet.setDuration(900);

        return transitionSet;
    }

    /**
     * 分享元素返回動畫
     * @return
     */
    private TransitionSet buildShareElemReturnSet() {
        TransitionSet transitionSet = new TransitionSet();

        Transition changePos = new ShareElemReturnChangePosition();
        changePos.addTarget(R.id.comment_box);
        transitionSet.addTransition(changePos);

        ChangeColor changeColor = new ChangeColor(getResources().getColor(R.color.white), getResources().getColor(R.color.black_85_alpha));
        changeColor.addTarget(R.id.comment_box);
        transitionSet.addTransition(changeColor);


        Transition revealTransition = new ShareElemReturnRevealTransition(mCommentBox);
        revealTransition.addTarget(R.id.comment_box);
        transitionSet.addTransition(revealTransition);

        transitionSet.setDuration(900);

        return transitionSet;
    }
}

結語

ok 關於自定義過場動畫基本就說完了。這裡沒有將具體如果使用過場動畫, 也沒有有說 EnterTransition 和 ReturnTransition 這些關系什麼的,還有如何最基本使用過場動畫什麼的, 這些Android 官網上都有中文文檔, 就不多提了

github 項目連接

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved