Android動畫之圓形揭露動畫Circular Reveal

1 Circular Reveal 圓形揭露動畫概述

Android5.0是一個里程碑式的版本,不但提升了流暢度,更是增加了很多炫酷的效果:

Touch feedback(觸摸反饋)
Reveal effect(揭露效果)
Activity transitions(Activity轉換效果)
Curved motion(曲線運動)
View state changes (視圖狀態改變)
Animate Vector Drawables(可繪矢量動畫)

今天主要講解揭露動畫。

RevealAnimator 揭露動畫,這時Android5.0添加的新動畫,動畫效果爲利用圓形縮放實現一個view的顯示或者隱藏,最低支持的版本爲api21,低版本使用會報異常,如果想要兼容api19以上的版本,可以自己實現這種縮放動畫。

因爲懶文章後面用到揭露動畫的地方沒有加版本限制,真實開發不能這麼做。

RevealAnimator 是個隱藏的類所以在AndroidDeveloper上沒有RevealAnimator 相關信息,所以只需要關注生成揭露動畫的函數。
Circular Reveal 圓形揭露動畫由ViewAnimationUtils.createCircularReveal生成。
public static Animator createCircularReveal(View view,
int centerX, int centerY, float startRadius, float endRadius)
參數說明:
view :要執行動畫效果的View
centerX:圓心x座標
centerY:圓心y座標
startRadius:開始時的圓半徑
endRadius:結束時的圓半徑
返回值是一個Animator。
一般要顯示把開始半徑設置爲0,結束設置響應值,如果要隱藏view,需要把開始半徑設置爲最大值,結束半徑設置爲0

重要特性:

  • 揭露動畫是一個異步動畫,它的回調方法都不能保證在準確的時間裏調用,但誤差不會很大。
  • 揭露對象要先於動畫開始前顯示(View.VISIBLE),因爲動畫開始時如果被操作對象處於隱藏狀態,那麼動畫就不會有效果,所以就算是不可見的對象,開始動畫前也需要設置爲可見。

2 createCircularReveal的簡單使用

首先要說明一點,看到api那麼簡單我直接應用到Activity中,但是換了很多個模擬器都無法出現揭露動畫效果,最終利用 ActivityOptionsCompat.makeSceneTransitionAnimation和startActivity(intent,activityOptionsCompat.toBundle());啓動的Activity才支持揭露動畫。

2.1 簡單使用,代碼示例

XML文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--做動畫的佈局-->
    <LinearLayout
        android:id="@+id/targetView"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:background="#ff00ff"
        android:orientation="vertical"
        android:gravity="center"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:textSize="20sp"
            android:text="我是內容區域 我是內容區域
                  我是內容區域 我是內容區域
                   我是內容區域
                   我是內容區域
                  我是內容區域我是內容區域" />
    </LinearLayout>

    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:text="開始動畫"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>

Activity文件

public class Main11Activity extends AppCompatActivity {
    LinearLayout targetView ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        Fade fade = new Fade();
        getWindow().setEnterTransition(fade);
        getWindow().setExitTransition(fade);
        getWindow().setReenterTransition(fade);
        getWindow().setReturnTransition(fade);

        ChangeBounds changeBounds = new ChangeBounds();
        changeBounds.setDuration(1000);
        ChangeClipBounds changeClipBounds = new ChangeClipBounds();
        changeClipBounds.setDuration(1000);
        ChangeImageTransform changeImageTransform = new ChangeImageTransform();
        changeImageTransform.setDuration(1000);
        ChangeTransform changeTransform = new ChangeTransform();
        changeTransform.setDuration(1000);
        getWindow().setSharedElementEnterTransition(changeBounds);
        getWindow().setSharedElementExitTransition(changeClipBounds);
        getWindow().setSharedElementReenterTransition(changeImageTransform);
        getWindow().setSharedElementReturnTransition(changeTransform);

        getWindow().setSharedElementsUseOverlay(true);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main11);

        targetView = findViewById(R.id.targetView);
    }

    @Override
    protected void onResume() {
        super.onResume();
        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final int width = targetView.getMeasuredWidth();
                final int height = targetView.getMeasuredHeight();
                final float radius = (float)Math.sqrt(width*width + height*height) / 2;//半徑
                Animator animator;
                if (targetView.getVisibility() == View.VISIBLE){
                    animator = ViewAnimationUtils.createCircularReveal(targetView, width/2, height/2, radius, 0);
                    animator.setDuration(1000);
                    animator.setInterpolator(new AccelerateDecelerateInterpolator());
                    animator.start();
                    animator.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            targetView.setVisibility(View.GONE);
                        }
                    });
                }else{
                    animator = ViewAnimationUtils.createCircularReveal(targetView, width/2, height/2, 0, radius);
                    animator.setDuration(1000);
                    animator.setInterpolator(new AccelerateDecelerateInterpolator());
                    targetView.setVisibility(View.VISIBLE);
                    animator.start();
                    animator.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            targetView.setVisibility(View.VISIBLE);
                        }
                    });
                }

            }
        });
    }
}

Activity代碼中關於轉場動畫的代碼不用管

效果圖:
在這裏插入圖片描述

2.2 對ImageView操作

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--做動畫的佈局-->
    <LinearLayout
        android:id="@+id/targetView"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:orientation="vertical"
        android:gravity="center"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

            <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitCenter"
            android:src="@drawable/image_home_game_nor"/>
    </LinearLayout>

    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:text="開始動畫"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>

效果圖:
在這裏插入圖片描述

注意:
可以看到代碼中在動畫開始之前設置了targetView可見( targetView.setVisibility(View.VISIBLE); animator.start();)因爲前面我們介紹了揭露動畫特性
重要特性:

  • 揭露動畫是一個異步動畫,它的回調方法都不能保證在準確的時間裏調用,但誤差不會很大。
  • 揭露對象要先於動畫開始前顯示(View.VISIBLE),因爲動畫開始時如果被操作對象處於隱藏狀態,那麼動畫就不會有效果,所以就算是不可見的對象,開始動畫前也需要設置爲可見。

所以可以在監聽動畫動畫開始回調時調用targetView.setVisibility(View.VISIBLE),但因爲揭露動畫是異步的,會有極小的概率導致view不可見,所以再動畫開始之前最保險。

3 結合屬性動畫,共享元素和揭露動畫的簡單實例

從ActivityA啓動ActivityB
ActivityA 佈局文件

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Main10Activity">

<ImageView
    android:id="@+id/floatactionbtn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/image_home_game_nor2"
    android:scaleType="centerCrop"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    android:transitionName="shareelement"/>

    <FrameLayout
        android:id="@+id/framelayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"
        android:background="@color/colorPrimary">

    </FrameLayout>

</android.support.constraint.ConstraintLayout>

ActivityA 代碼

public class ActivityA extends AppCompatActivity {

    private ImageView fab;
    private ConstraintLayout mCons;
    private FrameLayout mFram;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main10);
        fab = findViewById(R.id.floatactionbtn);
        mCons = findViewById(R.id.container);
        mFram = findViewById(R.id.framelayout);
        mFram.setVisibility(View.GONE);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Path path = new Path();
                path.moveTo(mCons.getMeasuredWidth()-fab.getWidth(),mCons.getMeasuredHeight() - fab.getHeight());
                path.quadTo(mCons.getMeasuredWidth()-300,mCons.getMeasuredHeight() -200,mCons.getMeasuredWidth()/2- fab.getWidth(),mCons.getMeasuredHeight()/2 - fab.getHeight()/2);
                ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(fab, "X", "Y", path);
                objectAnimator.setDuration(3000);
                objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

                Path path2 = new Path();
                path2.moveTo(1.0f,1.0f);
                path2.lineTo(2.0f,2.0f);
                path2.lineTo(1.0f,1.0f);
                path2.lineTo(2.0f,2.0f);
                path2.lineTo(1.0f,1.0f);
                ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(fab, View.SCALE_X,View.SCALE_Y, path2);
                objectAnimator2.setDuration(4000);
                objectAnimator2.setInterpolator(new AccelerateDecelerateInterpolator());
                objectAnimator2.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        Intent intent = new Intent(ActivityA.this, ActivityB.class);
                        ActivityOptionsCompat activityOptionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(ActivityA.this,fab,"shareelement");
                        startActivity(intent,activityOptionsCompat.toBundle());
                    }

                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                    }
                });

                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playSequentially(objectAnimator,objectAnimator2);
                animatorSet.start();
            }
        });
    }
}

ActivityB 佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/targetView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitCenter"
        android:src="@drawable/image_home_game_nor"
        android:transitionName="shareelement" />
    <!--做動畫的佈局-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:orientation="vertical"
        android:gravity="center"
        android:visibility="gone"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

            <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitCenter"
            android:src="@drawable/image_home_game_nor"/>
    </LinearLayout>

    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:text="開始動畫"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>

ActivityB 代碼

public class ActivityB extends AppCompatActivity {
    View targetView ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main11);

        targetView = findViewById(R.id.targetView);
    }

    @Override
    protected void onResume() {
        super.onResume();
        findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Animator animator;
                final int width = targetView.getMeasuredWidth();
                final int height = targetView.getMeasuredHeight();
                final float radius = (float)Math.sqrt(width*width + height*height) / 2;//半徑
                if (targetView.getVisibility() == View.VISIBLE){
                    animator = ViewAnimationUtils.createCircularReveal(targetView, width/2, height/2, radius, 0);
                    animator.setDuration(1000);
                    animator.setInterpolator(new AccelerateDecelerateInterpolator());
                    animator.start();
                    animator.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            targetView.setVisibility(View.GONE);
                        }
                    });
                }else{
                    animator = ViewAnimationUtils.createCircularReveal(targetView, width/2, height/2, 0, radius);
                    animator.setDuration(1000);
                    animator.setInterpolator(new AccelerateDecelerateInterpolator());
                    targetView.setVisibility(View.VISIBLE);
                    animator.start();
                    animator.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            targetView.setVisibility(View.VISIBLE);
                        }
                    });
                }

            }
        });

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                final int width = targetView.getMeasuredWidth();
                final int height = targetView.getMeasuredHeight();
                final float radius = (float)Math.sqrt(width*width + height*height) / 2;//半徑
                Animator animator;
                animator = ViewAnimationUtils.createCircularReveal(targetView, width/2, height/2, radius, 20);
                animator.setDuration(3000);
                animator.setInterpolator(new AccelerateDecelerateInterpolator());
                targetView.setVisibility(View.VISIBLE);
                animator.start();
                animator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        targetView.setVisibility(View.GONE);
                    }
                });
            }
        },1000);
    }
}

效果圖:
在這裏插入圖片描述

有沒有兼容低版本的庫,大家給推薦個?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章