天天看點

使用CircularReveal動畫效果切換頁面

本文位址: http://blog.csdn.net/caroline_wendy/article/details/50756717

歡迎Follow我的GitHub, 關注我的CSDN.
使用CircularReveal動畫效果切換頁面

本文的合集已經編著成書,進階Android開發強化實戰,歡迎各位讀友的建議和指導。在京東即可購買:https://item.jd.com/12385680.html

使用CircularReveal動畫效果切換頁面

Android的Material Design設計理念, 帶來很多絢麗的動畫效果. 在頁面切換中, 最常用的就是SharedElementTransition, 通過設定控件的變換方式, 在進入時把控件變換為頁面, 在退出時, 把頁面變換為控件, 同時, 可以設定控件移動的軌迹. 這樣的控件, 可以應用于消息通知, 或者廣告顯示, 提供非常好的使用者體驗. 那麼是如何實作的呢?

随着廠商的版本疊代, 超過三分之一的手機都是5.0以上的作業系統, 随着更多便宜的低端手機普及5.0+系統(如紅米系列), 給使用者帶來更好的體驗, 會大大增加應用留存率.
使用CircularReveal動畫效果切換頁面

本文主要内容:

(1) 修改分享元素的滑動軌迹, 以90°圓弧方式出現和傳回, 即transition動畫.

(2) 生成和銷毀頁面的爆炸和凝聚效果, 即CircularReveal的使用方式.

本文源碼的GitHub下載下傳位址.

最終動畫效果

使用CircularReveal動畫效果切換頁面

1. 首頁

建立一個含義Fab按鈕的HelloWorld工程. 添加ButterKnife和Lambda庫.

設定Fab按鈕的跳轉事件.

// Fab的跳轉事件
    public void startOtherActivity(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ActivityOptions options =
                    ActivityOptions.makeSceneTransitionAnimation(this, mFab, mFab.getTransitionName());
            startActivity(new Intent(this, OtherActivity.class), options.toBundle());
        } else {
            startActivity(new Intent(this, OtherActivity.class));
        }
    }
           
確定版本号大于5.0, 支援Material Design的動畫效果.

設定Fab的TransitionName, 即變化名稱.

<android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:clickable="true"
        android:onClick="startOtherActivity"
        android:src="@android:drawable/ic_dialog_email"
        android:transitionName="@string/other_transition_name"
        tools:targetApi="lollipop"/>
           
注意android:transitionName屬性, 表示變換名稱.

2. 跳轉頁

頁面由背景, 變化控件, 關閉控件, 這三部分組成.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/other_rl_container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent">

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/other_fab_circle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:transitionName="@string/other_transition_name"
        app:backgroundTint="@color/colorAccent"
        app:elevation="0dp"
        app:fabSize="normal"
        app:pressedTranslationZ="8dp"
        tools:targetApi="21"/>

    <TextView
        android:id="@+id/other_tv_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="@string/other_text"
        android:textColor="@android:color/white"
        android:textSize="40sp"
        android:textStyle="bold"
        android:visibility="invisible"
        tools:visibility="visible"/>

    <ImageView
        android:id="@+id/other_iv_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:contentDescription="@null"
        android:onClick="backActivity"
        android:src="@drawable/ic_close_white"
        android:visibility="invisible"/>

</RelativeLayout>
           
注意, 變換控件FloatingActionButton與首頁的變換控件有相同的transitionName.

顯示邏輯, 設定入場和退場動畫, 爆炸的動畫效果.

@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other);
        ButterKnife.bind(this);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setupEnterAnimation(); // 入場動畫
            setupExitAnimation(); // 退場動畫
        } else {
            initViews();
        }
    }
           

退出邏輯, 退出動畫, 凝聚的動畫效果.

// 退出按鈕
    public void backActivity(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            onBackPressed();
        } else {
            defaultBackPressed();
        }
    }
           

下面仔細分析顯示和退場動畫.

3. 顯示動畫

分享元素切換使用弧度規矩, 即arc_motion, 90°旋轉過去和回來. 在動畫結束後, 頁面顯示使用爆炸效果.

// 入場動畫
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void setupEnterAnimation() {
        Transition transition = TransitionInflater.from(this)
                .inflateTransition(R.transition.arc_motion);
        getWindow().setSharedElementEnterTransition(transition);
        transition.addListener(new Transition.TransitionListener() {
            @Override public void onTransitionStart(Transition transition) {

            }

            @Override public void onTransitionEnd(Transition transition) {
                transition.removeListener(this);
                animateRevealShow();
            }

            @Override public void onTransitionCancel(Transition transition) {

            }

            @Override public void onTransitionPause(Transition transition) {

            }

            @Override public void onTransitionResume(Transition transition) {

            }
        });
    }

    // 動畫展示
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void animateRevealShow() {
        GuiUtils.animateRevealShow(
                this, mRlContainer,
                mFabCircle.getWidth() / , R.color.colorAccent,
                new GuiUtils.OnRevealAnimationListener() {
                    @Override public void onRevealHide() {

                    }

                    @Override public void onRevealShow() {
                        initViews();
                    }
                });
    }
           

arc_motion角度變換

<transitionSet
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500"
    android:interpolator="@android:interpolator/linear_out_slow_in">

    <changeBounds>
        <!--suppress AndroidElementNotAllowed -->
        <arcMotion
            android:maximumAngle="90"
            android:minimumHorizontalAngle="90"
            android:minimumVerticalAngle="0"/>
    </changeBounds>
</transitionSet>
           

爆炸的動畫效果

// 圓圈爆炸效果顯示
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static void animateRevealShow(
            final Context context, final View view,
            final int startRadius, @ColorRes int color,
            OnRevealAnimationListener listener) {
        int cx = (view.getLeft() + view.getRight()) / ;
        int cy = (view.getTop() + view.getBottom()) / ;

        float finalRadius = (float) Math.hypot(view.getWidth(), view.getHeight());

        // 設定圓形顯示動畫
        Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, startRadius, finalRadius);
        anim.setDuration();
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        anim.addListener(new AnimatorListenerAdapter() {
            @Override public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                view.setVisibility(View.VISIBLE);
                listener.onRevealShow();
            }

            @Override public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                view.setBackgroundColor(ContextCompat.getColor(context, color));
            }
        });

        anim.start();
    }
           

使用CircularReveal, 即圓形顯示的動畫效果.

第一個參數是顯示的視圖, 第二個和第三個是變換的中心位置, 第三個和第四個是變換的起始半徑和結束半徑.

4. 退出動畫

退出動畫是凝聚效果, 同樣使用的是CircularReveal.

// 退出事件
    @Override public void onBackPressed() {
        GuiUtils.animateRevealHide(
                this, mRlContainer,
                mFabCircle.getWidth() / , R.color.colorAccent,
                new GuiUtils.OnRevealAnimationListener() {
                    @Override
                    public void onRevealHide() {
                        defaultBackPressed();
                    }

                    @Override
                    public void onRevealShow() {

                    }
                });
    }
           

退出和顯示的差別就是起始和終止的半徑不同.

// 圓圈凝聚效果
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public static void animateRevealHide(
            final Context context, final View view,
            final int finalRadius, @ColorRes int color,
            OnRevealAnimationListener listener
    ) {
        int cx = (view.getLeft() + view.getRight()) / ;
        int cy = (view.getTop() + view.getBottom()) / ;
        int initialRadius = view.getWidth();
        // 與入場動畫的差別就是圓圈起始和終止的半徑相反
        Animator anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius, finalRadius);
        anim.setDuration();
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        anim.addListener(new AnimatorListenerAdapter() {
            @Override public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                view.setBackgroundColor(ContextCompat.getColor(context, color));
            }

            @Override public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                listener.onRevealHide();
                view.setVisibility(View.INVISIBLE);
            }
        });
        anim.start();
    }
           

最後說一些有關使用者體驗的事情, 對于一款應用而言, 好的性能固然重要, 但優秀的設計也非常關鍵, 優異的産品需要優異的展現方式.

OK, that’s all! Enjoy it!