1. 程式人生 > >Android轉場動畫

Android轉場動畫

Android轉場動畫

課程目標:

1. 掌握定義轉場動畫的方法

 

學習內容:

1. 轉場動畫的作用

2. 哪些時機需要轉場動畫

3. 轉動畫的方法

 

一、 轉場動畫的作用

1. 轉場動畫可以提供視覺連續性。

 

二、 哪些時機需要轉場動畫

1. 視覺狀態改變時。

   a. 單個檢視變化時

   b. 佈局變化時

   c. activity跳轉時

 

三、 轉場動畫的方法

1. 揭露效果(單個檢視的轉場動畫)

a. View狀態改變時(下面用一個demo演示當一個View在可見和不可見時產生揭露效果)

 

實踐:

xml佈局檔案: activity_reveal.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/activity_view"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context="com.imooc.transitionanimation.reveal.RevealActivity">



    <CheckBox

        android:text="Play animation"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:id="@+id/checkBox" />



    <View

        android:id="@+id/view"

        android:layout_centerInParent="true"

        android:background="@color/colorAccent"

        android:onClick="onClick"

        android:visibility="invisible"

        android:layout_width="300dp"

        android:layout_height="300dp"/>



    <Button

        android:onClick="onClick"

        android:id="@+id/buttonChangeVisibility"

        android:layout_width="wrap_content"

        android:text="switch"

        android:layout_alignParentBottom="true"

        android:layout_alignParentEnd="true"

        style="?android:buttonBarButtonStyle"

        android:layout_height="wrap_content" />



</RelativeLayout>

java程式碼: RecealActivity.java

public class RevealActivity extends AppCompatActivity {



    private static final String TAG = "RevealActivity";



    private View mView;

    private CheckBox mPlayAnimationCheckBox;



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_reveal);

        mView = findViewById(R.id.view);

        mPlayAnimationCheckBox = (CheckBox) findViewById(R.id.checkBox);

    }



    public void onClick(View view) {

        final boolean playAnimation = mPlayAnimationCheckBox.isChecked();

        switch (view.getId()) {

            case R.id.buttonChangeVisibility:

                handleChangeVisibility(playAnimation);

                break;

        }

    }



    private void handleChangeVisibility(boolean playAnimation) {

        Log.d(TAG, "handleChangeVisibility() called with: playAnimation = [" + playAnimation + "]");

        Log.d(TAG, "handleChangeVisibility: " + mView.isShown());

        if (playAnimation) {

                      // 有揭露效果時View的變化效果

            if (mView.isShown()) {

                revealExit();

            } else {

                revealEnter();

            }

        } else {

           // 沒有揭露效果時View的變化效果

           if (mView.isShown()) {

               mView.setVisibility(View.INVISIBLE);

           } else {

               mView.setVisibility(View.VISIBLE);

           }

        }

    }



    // 通過揭露效果顯示View

    private void revealEnter() {

        int w = mView.getWidth();

        int h = mView.getHeight();



        int cx = w;

        int cy = h;



        int r = (int) Math.hypot(w, h);

        // 1. 建立Animator 物件(mView: 顯示揭露效果的物件  cx,cy: 產生介面效果的圓心(也就是起始點)  0: 動畫開始前半徑為0  r: 動畫結束的半徑)

        Animator animator = ViewAnimationUtils.createCircularReveal(mView, cx, cy, 0, r);



        mView.setVisibility(View.VISIBLE);



        animator.start();

    }



       // 通過揭露效果隱藏View

    private void revealExit() {

        int w = mView.getWidth();

        int h = mView.getHeight();



        int cx = w;

        int cy = h;



        int r = (int) Math.hypot(w, h);

        // 1. 建立Animator 物件(mView: 顯示揭露效果的物件  cx,cy: 產生介面效果的圓心(也就是起始點)  r: 動畫開始前半徑為0  0: 動畫結束的半徑)

        Animator animator = ViewAnimationUtils.createCircularReveal(mView, cx, cy, r, 0);



        animator.setDuration(5000);



        // 給Animator物件註冊監聽器,讓其在動畫結束時,View不可見

        animator.addListener(new AnimatorListenerAdapter() {

            @Override

            public void onAnimationEnd(Animator animation) {

                super.onAnimationEnd(animation);

                mView.setVisibility(View.INVISIBLE);

            }

        });



        animator.start();

    }



}

2.多檢視的轉場動畫

為了完成多檢視的轉場動畫,Android中為我們提供了android.transition包。在這個包下

主要提供三個類:

1. Scene

2. Transition

3. TransitionManager

上面這三個類之間的關係: Scene就是場景,Transition就是轉換型別,TransitionManager負責場景的轉換,並將不同的轉換型別結合到場景轉換的過程中。

 

下面分別來看下這三個類:

1. Scene(場景)

我們螢幕上顯示的實際上就是一個View樹結構,我們看到的檢視其實就是這個View樹結構中所有View所處於的某種狀態的瞬間,這就是一個場景(Scene)。也就是說我們可以把檢視的某一個狀態稱為一個場景。

獲取這個場景的scene物件(從佈局中載入一個場景):

scene.getSceneForLayout(sceneRoot, R.layout.scene_overview, this);

引數含義:

sceneRoot:轉場發生的地方

R.layout.scene_overview:組成這一場景的佈局資源

this:上下文

 

2. Transition(轉換型別)

Android中為我們預定好了一些轉換效果,比如:

Fade(淡入淡出):也就是透明度變化

ChangeBounds(改變邊界)

AutoTransition(預設自動使用這個效果)

 

獲取Transition物件:

TransitionInflater.from(getBaseContext()).inflateTransition(R.transition.transition);

transition和我們之前的檢視物件中Animation以及屬性動畫中的Animator類似,他們都可以通過xml檔案和java程式碼定義,推薦使用xml檔案。

 

3. TransitionManager

有了場景物件和轉換型別,接著就可以進行轉場動畫了:

TransitionManager.go(mTargetScene, transition);

引數含義:

mTargetScene: 我們要跳轉的目標場景

transition:   使用的轉換型別,可以使我們自定義的,也可以用Android中提供的,如果不設定這個引數,預設使用AutoTransition這個場景。

 

實踐:

1.使用xml檔案自定義一個Transition物件,在res目錄下建立一個目錄/res/transition/, 然後建立一個xml檔案。(/res/transition/transition.xml):

<?xml version="1.0" encoding="utf-8"?>

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">

    <changeImageTransform android:duration="3000">

        <targets android:targetId="@id/image" />

    </changeImageTransform>



    <fade android:duration="3000" android:startDelay="1000">

    </fade>

</transitionSet>

activity佈局檔案( activity_scene.xml ):

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/activity_scene"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context="com.imooc.transitionanimation.scene.SceneActivity">



    <FrameLayout

        android:id="@+id/scene_root"

        android:layout_width="match_parent"

        android:layout_height="match_parent" />



</RelativeLayout>

進入場景xml佈局檔案( scene_overview.xml ):

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/activity_scene"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    tools:context="com.imooc.transitionanimation.scene.SceneActivity">



    <ImageView

        android:layout_width="match_parent"

        android:id="@+id/image"

        android:layout_height="0dp"

        android:layout_weight="1"

        android:src="@drawable/chang_bai"

        android:transitionName="@string/pic"

        />



    <RelativeLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginTop="16dp">



        <ImageButton

            android:id="@+id/btnInfo"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_alignParentEnd="true"

            android:layout_marginTop="4dp"

            android:background="?android:selectableItemBackground"

            android:onClick="onClick"

            android:src="@drawable/ic_info_black_24dp" />



        <TextView

            android:id="@+id/tvTitle"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:layout_toStartOf="@id/btnInfo"

            android:text="@string/title"

            android:textAppearance="@style/TextAppearance.AppCompat.Headline" />



        <TextView

            android:id="@+id/tvAddress"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_below="@id/tvTitle"

            android:layout_marginTop="8dp"

            android:text="@string/address"

            android:textAppearance="@style/TextAppearance.AppCompat.Medium" />



    </RelativeLayout>



</LinearLayout>

退出場景xml佈局檔案( scene_info.xml ):

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/activity_scene"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    tools:context="com.imooc.transitionanimation.scene.SceneActivity">



    <ImageView

        android:id="@+id/image"

        android:layout_width="160dp"

        android:layout_height="120dp"

        android:src="@drawable/chang_bai" />



    <RelativeLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_marginTop="16dp">



        <ImageButton

            android:id="@+id/btnClose"

            android:transitionName="@string/icon"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_alignParentEnd="true"

            android:layout_marginTop="4dp"

            android:background="?android:selectableItemBackground"

            android:onClick="onClick"

            android:src="@drawable/ic_close_black_24dp" />



        <TextView

            android:id="@+id/tvTitle"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:layout_toStartOf="@id/btnClose"

            android:text="@string/title"

            android:textAppearance="@style/TextAppearance.AppCompat.Title" />



        <TextView

            android:id="@+id/tvAddress"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_below="@id/tvTitle"

            android:layout_marginTop="8dp"

            android:text="@string/address"

            android:textAppearance="@style/TextAppearance.AppCompat.Medium" />



        <TextView

            android:id="@+id/tvInfo"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:layout_below="@id/tvAddress"

            android:layout_marginTop="8dp"

            android:text="@string/info" />



    </RelativeLayout>



</LinearLayout>

2.java程式碼:

public class SceneActivity extends AppCompatActivity {

    private Scene mOverViewScene;

    private Scene mInfoScene;



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_scene);



        ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

        // 獲取進入時的場景物件

        mOverViewScene = Scene.getSceneForLayout(sceneRoot, R.layout.scene_overview, getBaseContext());

               //  獲取退出時的場景物件

        mInfoScene = Scene.getSceneForLayout(sceneRoot, R.layout.scene_info, getBaseContext());



        // 第一次載入佈局時使用的場景轉換,如果沒有Transition引數,預設使用AutoTransition轉換型別

        TransitionManager.go(mOverViewScene);

    }



    public void onClick(View view) {

        switch (view.getId()) {

            case R.id.btnInfo:

                // 自定義場景轉換型別transition

                Transition transition = TransitionInflater.from(getBaseContext())

                        .inflateTransition(R.transition.transition);

                TransitionManager.go(mInfoScene, transition);

                break;

            case R.id.btnClose:

                TransitionManager.go(mOverViewScene);

                break;

        }

    }



}

3.Activity間轉場動畫:

ActivityA和ActivityB之間跳轉的動畫涉及到3次動畫:

ActivityA跳轉到ActivityB時,ActivityA就會有離場動畫(Exit),進入到ActivityB時ActivityB就有進場動畫(Enter),當ActivityB按Back鍵返回到ActivityA時,ActivityA就又被再次進入,此時又是一種動畫效果(Re_enter)。

 

使用轉場動畫: (ActivityOptions)

ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this);

startActivity(intent, options.toBundle());

 

轉場動畫的動畫型別: (Transition)

Android系統中預設的有三種:

1. Fade(淡入淡出)

2. Slide(滑動效果)

3. Explode(爆炸效果)

 

實踐:

a. xml佈局檔案:

activity_first.xml:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/activity_first"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context="com.imooc.transitionanimation.activity.FirstActivity">



    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content">



        <ImageView

            android:id="@+id/iv1"

            android:layout_width="0dp"

            android:layout_height="160dp"

            android:layout_weight="1"

            android:background="?attr/selectableItemBackground"

            android:onClick="onClick"

            android:src="@drawable/pic1" />



        <ImageView

            android:id="@+id/iv2"

            android:layout_width="0dp"

            android:layout_height="160dp"

            android:layout_weight="1"

            android:background="?attr/selectableItemBackground"

            android:onClick="onClick"

            android:src="@drawable/pic2" />

    </LinearLayout>



    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content">



        <ImageView

            android:id="@+id/iv3"

            android:layout_width="0dp"

            android:layout_height="160dp"

            android:layout_weight="1"

            android:background="?attr/selectableItemBackground"

            android:onClick="onClick"

            android:src="@drawable/pic3" />



        <ImageView

            android:id="@+id/iv4"

            android:layout_width="0dp"

            android:layout_height="160dp"

            android:layout_weight="1"

            android:background="?attr/selectableItemBackground"

            android:onClick="onClick"

            android:src="@drawable/pic4" />



    </LinearLayout>



</LinearLayout>

activity_second.xml:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/activity_second"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context="com.imooc.transitionanimation.activity.SecondActivity">



    <ImageView

        android:id="@+id/iv"

        <!-- 共享元素定義的名稱,要和java程式碼中相同 -->

        android:transitionName="img"

        android:layout_centerInParent="true"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content" />



</RelativeLayout>

b. java程式碼:

FirstActivity.java:

public class FirstActivity extends Activity {



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_first);

    }



    public void onClick(View view) {

        int resId = -1;

        switch (view.getId()) {

            case R.id.iv1:

                resId = R.drawable.pic1;

                break;

            case R.id.iv2:

                resId = R.drawable.pic2;

                break;

            case R.id.iv3:

                resId = R.drawable.pic3;

                break;

            case R.id.iv4:

                resId = R.drawable.pic4;

                break;

        }

        Intent intent = new Intent(this, SecondActivity.class);

        intent.putExtra("resId", resId);

        // 定義動畫的型別(爆炸效果)

        Transition transition = new Explode();

        // 去除不想實現動畫效果的view(這裡設定的是狀態列不參加動畫效果)

        transition.excludeTarget(android.R.id.statusBarBackground, true);

        

        // 設定進場動畫

        getWindow().setEnterTransition(transition);

        // 設定離場動畫

        getWindow().setExitTransition(transition);

        // 設定再次進入時的動畫

        getWindow().setReenterTransition(transition);

        // 為共享元素設定特定的動畫效果

        getWindow().setSharedElementEnterTransition(transition);



        // 定義共享元素(注意這裡的名稱"img"要和xml佈局檔案中的view transitionName屬性名稱一樣)

        Pair<View, String> shareElement = Pair.create(view, "img");

        // 建立實現轉場動畫需要的options物件

        ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, shareElement);

        startActivity(intent, options.toBundle());

    }

}

SecondActivity.java:

public class SecondActivity extends Activity {



    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_second);

        int resId = getIntent().getExtras().getInt("resId");

        ImageView iv = (ImageView) findViewById(R.id.iv);

        iv.setTransitionName("img");

        iv.setImageResource(resId);

        

        // 下面這些含義解釋如上

        Transition transition = new Explode();

        transition.excludeTarget(android.R.id.statusBarBackground, true);

        getWindow().setEnterTransition(transition);

        getWindow().setExitTransition(transition);

    }

}