[AS3.0.1]Android 5.0新特性跳轉動畫Transition

前言

很早的時候算是看過一些關於Android 5.0的新特性,但是當時也就是記錄一下用法,也沒後續去看,前段時間看到一個動畫切換效果,我還傻乎乎的去寫,後來發現居然是Android自帶的,找了些資料算是補習了!
本篇包括TransitionShared ElementCircular Reveal的內容。


Transition

Transition是Android 5.0新加入的過渡動畫效果,包括ExplodeSlideFade

Explode Slide Fade
從中心移入或移出 從邊緣移入或移出 調整透明度產生漸變
Explode Slide Fade

在講解這些功能之前我們需要知道幾個方法

  • setEnterTransition(Transition transition)
    設置當前頁面進入的過渡動畫
  • setReturnTransition(Transition transition)
    設置當前頁面返回的過渡動畫
  • setExitTransition(Transition transition)
    設置當前頁面退出的過渡動畫
  • setReenterTransition(Transition transition)
    設置當前頁面重啓的過渡動畫

這幾個方法是設置過渡動畫的時候需要注意的。

Explode

我們這邊需要做一個A -> B 然後B進行Explode過渡動畫的效果設置。
邏輯如下:
A頁面進入B頁面 B頁面需要有過渡動畫 所以只要對B頁面設置setEnterTransition方法和setReturnTransition方法即
可。

調用方法也要傳入對應的代碼纔有效

Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Explode");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());

當直接使用ActivityOptions.makeSceneTransitionAnimation(activity)的時候在低於5.0以下的時候軟件會直接崩潰,所以可以使用ActivityOptionsCompat來兼容低版本,但是這樣設置只是讓低版本不會崩潰,並不會有過渡動畫效果,後面我會用ActivityOptionsCompat做演示。

在接受到的頁面加入以下判斷


        transition = getIntent().getStringExtra("transition");

        if (transition != null) {
            switch (transition) {
                case "Explode":
                    Explode explode = new Explode();
                    setDuration(explode);
                    getWindow().setEnterTransition(explode);
                    break;
            }
        }

至此就實現了功能,不設置setReturnTransition系統會自動調用進入的反向動畫來實現!

Slide

和上面的Explode一樣我們只要加入以下代碼

 Intent intent = new Intent(activity, TransitionActivity.class);
 intent.putExtra("transition", "Slide");
 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());

在需要展示的頁面加入判斷,不過由於Slide是需要設置到底從哪個方向的所以可以加入方向

        transition = getIntent().getStringExtra("transition");

        if (transition != null) {
            switch (transition) {
            ...
                case "Slide":
                    Slide slide = new Slide();
                    slide.setSlideEdge(Gravity.RIGHT);
                    setDuration(slide);
                    getWindow().setEnterTransition(slide);
                    break;
            }
        }

Fade

這個過渡也沒有特別的直接調用以下代碼就可以了

 Intent intent = new Intent(activity, TransitionActivity.class);
 intent.putExtra("transition", "Fade");
 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());

設置展示

        transition = getIntent().getStringExtra("transition");

        if (transition != null) {
            switch (transition) {
            ...
                case "Fade":
                    Fade fade = new Fade();
                    setDuration(fade);
                    getWindow().setEnterTransition(fade);
                    break;
            }
        }

補充setReturnTransition

至此可以發現使用特別簡單。下面我們設置setReturnTransition方法來實現進入和退出不一樣的效果
在這裏插入圖片描述

可以看到進入是一個有回彈效果的,並且退出的方向也和進入的相同了!

打開頁面的代碼也沒有特別的變化

Intent intent = new Intent(activity, TransitionActivity.class);
intent.putExtra("transition", "Slide2");
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());

展示的代碼

        transition = getIntent().getStringExtra("transition");

        if (transition != null) {
            switch (transition) {
            ...
                case "Slide2":
                    Slide slide2 = new Slide();
                    slide2.setSlideEdge(Gravity.RIGHT);
                    setDuration(slide2);
                    //回彈效果
                    slide2.setInterpolator(new BounceInterpolator());
                    getWindow().setEnterTransition(slide2);
                    break;
            }
        }

前面的代碼都沒有特別的變化!


只是在最後返回的時候對setReturnTransition進行了修改。

    @Override
    public void onBackPressed() {
        if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // 如果沒有 return transition 被定義,將使用反進入的動畫
            switch (transition) {
                case "Slide2":
                    Slide slide = new Slide();
                    slide.setSlideEdge(Gravity.LEFT);
                    setDuration(slide);
                    getWindow().setReturnTransition(slide);
                    break;
            }
            //一定要調用finishAfterTransition
            finishAfterTransition();
        } else {
            super.onBackPressed();
        }
    }

這邊需要注意的就是調用的關閉頁面方法一定要是finishAfterTransition纔會執行過渡動畫。


Shared Element

上面講完了Transition現在來說下Shared Element就是共享元素。
首先我們看下效果
Shared Element

這就是元素共享的界面過渡動畫效果,下面我們看下如何實現!

單獨元素並且不傳遞圖片

這次我們使用ActivityCompatActivityOptionsCompat來實現 這是爲了兼容低版本,讓低版本不崩潰而已!效果是沒辦法實現!

首先佈局頁面代碼爲

    <TextView
        android:id="@+id/tv_1_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="24dp"
        android:background="@android:color/holo_blue_bright"
        android:gravity="center"
        android:text="點擊單獨放大"
        android:transitionName="Open"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

我們發現佈局上面加入了一段android:transitionName="Open"這是爲了讓元素進行共享的起點。


打開頁面的代碼如下

tv1 = findViewById(R.id.tv_1_ase);

Intent intent = new Intent(activity, SharedActivity.class);
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, tv1,
        ViewCompat.getTransitionName(tv1));
ActivityCompat.startActivity(activity, intent, compat.toBundle());

我們對比上面的代碼會發現也是調用makeSceneTransitionAnimation方法,不過傳遞的不在是一個Explode這類對象了,而是傳遞了一個view 並且還傳遞了TransitionName,這個可以直接寫或者取!


之後是我們需要展示效果的頁面
需要進行過渡動畫的View佈局代碼

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="200dp"
        android:transitionName="Open"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/homepage_banner2" />

這樣我們兩邊就都有了一個android:transitionName="Open"這樣進行跳轉之後系統就會幫我們綁定兩個View

對於展示頁面我們可以不需要寫任何代碼!

多個元素並且不傳遞圖片

多個元素其實也是一樣的,只不過傳遞的view多了而已!

首先還是佈局,我們寫入了兩個transitionName
進入前的佈局

    <TextView
        android:id="@+id/tv_1_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="24dp"
        android:background="@android:color/holo_blue_bright"
        android:gravity="center"
        android:text="點擊單獨放大"
        android:transitionName="Open"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_2_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="24dp"
        android:background="@android:color/holo_orange_dark"
        android:gravity="center"
        android:text="點擊放大2個"
        android:transitionName="Open2"
        app:layout_constraintStart_toEndOf="@+id/tv_1_ase"
        app:layout_constraintTop_toTopOf="@+id/tv_1_ase" />

進入後的佈局

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="200dp"
        android:transitionName="Open"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/homepage_banner2" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:transitionName="Open2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        app:srcCompat="@mipmap/homepage_banner1" />

這邊各自對應了各自的android:transitionName


跳轉代碼

Pair<View, String> p1 = new Pair<View, String>(tv1, ViewCompat.getTransitionName(tv1));
Pair<View, String> p2 = new Pair<View, String>(tv2, ViewCompat.getTransitionName(tv2));
ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, p1, p2);
Intent intent = new Intent(activity, SharedActivity.class);
ActivityCompat.startActivity(activity, intent, compat.toBundle());

我們創建多個Pair對象在傳入就可以了

元素傳遞圖片

上面都是內部本身就有了圖片,外部只是一個位置提供而已,那麼能夠在外部傳入圖片進去麼。是可以的,其實效果只是告訴第二個頁面進入之後你的圖片需要設置什麼而已,實際其實是一樣的!

我們新增兩個按鈕佈局

    <TextView
        android:id="@+id/tv_3_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginTop="24dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="自定義圖片1"
        android:transitionName="Open3"
        app:layout_constraintStart_toStartOf="@+id/tv_1_ase"
        app:layout_constraintTop_toBottomOf="@+id/tv_1_ase" />

    <TextView
        android:id="@+id/tv_4_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginTop="24dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="自定義圖片2"
        android:transitionName="Open3"
        app:layout_constraintEnd_toEndOf="@+id/tv_2_ase"
        app:layout_constraintTop_toBottomOf="@+id/tv_2_ase" />

並且在跳轉頁面也加入一個佈局用於展示

    <ImageView
        android:id="@+id/imageView3"
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:transitionName="Open3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

這是一個沒有設置圖片的View,當進入之後才設置圖片

跳轉代碼如下

tv3.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                new Pair<View, String>(tv3, ViewCompat.getTransitionName(tv3))
        );
        Intent intent = new Intent(activity, SharedActivity.class);
        intent.putExtra("imgid", R.mipmap.ic_launcher_round);
        ActivityCompat.startActivity(activity, intent, compat.toBundle());
    }
});

tv4.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                new Pair<View, String>(tv4, ViewCompat.getTransitionName(tv4))
        );
        Intent intent = new Intent(activity, SharedActivity.class);
        intent.putExtra("imgid", R.mipmap.ic_launcher);
        ActivityCompat.startActivity(activity, intent, compat.toBundle());
    }
});

這次我們就需要設置圖片了,所以展示頁面代碼如下

        img3 = findViewById(R.id.imageView3);
        imgId = getIntent().getIntExtra("imgid", -1);
        if (imgId != -1) {
            Glide.with(this).load(imgId).into(img3);
        }

這樣設置之後我們進入之後就是傳遞的圖片了!

注:當然也可以是網絡圖片,不過由於網絡的原因,所以建議是直接先傳在跳轉前的圖片,進入之後等網絡自己刷新新的圖片就好了!

補充1 監聽兩個跳轉頁面直接的關聯

有可能會做一個輪播的點擊跳轉的,然後內部切換之後返回回來就會發現位置明顯不對或者外部的輪播圖片並未改變,這個時候我們只要加入監聽就好了!

邏輯如下:
A 在a圖片的時候 進入 B 並且打開了a圖片,這個時候B 切換了下圖片變成了b圖片,這個時候返回需要告訴A圖片換了。


首先在A的onCreate中加入監聽

setExitSharedElementCallback(new SharedElementCallback() {
    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
        if (bundle != null) {
            int id = bundle.getInt("imgId");
            String url = bundle.getString("imgUrl");
            Log.e("-s-", "id = " + id);
            Log.e("-s-", "url = " + url);
            bundle = null;
        }
    }
});

然後實現Activity的onActivityReenter方法,即頁面跳轉回來之後判斷傳回來的data

    @Override
    public void onActivityReenter(int resultCode, Intent data) {
        Log.e("-s-", "resultCode = " + resultCode);
        if (resultCode == 101) {
            bundle = new Bundle(data.getExtras());
        }
    }

至此A頁面的監聽寫完了,再來看下B頁面的傳遞信息
將傳遞信息的代碼寫在finishAfterTransition方法中

    @Override
    public void finishAfterTransition() {
        Intent data = new Intent();
        data.putExtra("imgId", imgId);
        data.putExtra("imgUrl", imgUrl);
        setResult(101, data);
        super.finishAfterTransition();
    }

這樣就實現了兩個Activity之間的傳遞信息功能!

補充2 將TransitionShared Element結合下

實現多種結合的過渡動畫,效果如下
Transition+Shared Element

跳轉代碼

        tv9.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, SharedActivity.class);
                intent.putExtra("transition", "Slide+");
                intent.putExtra("imgurl", imgs[0]);
                ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                        new Pair<View, String>(tv9, ViewCompat.getTransitionName(tv9))
                );
                ActivityCompat.startActivity(activity, intent, compat.toBundle());
            }
        });

和上面比就是加入了一些參數而已。
實現頁面代碼

        img3 = findViewById(R.id.imageView3);

        imgId = getIntent().getIntExtra("imgid", -1);
        imgUrl = getIntent().getStringExtra("imgurl");

        transition = getIntent().getStringExtra("transition");
        if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            switch (transition) {
                case "Slide+":
                    Slide slide = new Slide();
                    slide.setSlideEdge(Gravity.RIGHT);
                    getWindow().setEnterTransition(slide);
                    break;
            }
        }
        if (imgUrl != null) {
            Glide.with(this).load(imgUrl).into(img3);
        } else if (imgId != -1) {
            Glide.with(this).load(imgId).into(img3);
        }

就是將Transition和SharedElement簡單結合而已。


Circular Reveal

這個效果是在當前頁面進行一個擴散過渡。但是可以做到加入到跳轉動畫中!
效果如下
Circular Reveal

基本說明

先說下最基本的使用說明
其實就是調用ViewAnimationUtils.createCircularReveal創建一個Animator,說白了就是一個Animator,只不過官方提供了一個擴散效果動畫給你而已_(:з」∠)_
createCircularReveal提供了5個參數View view, int centerX, int centerY, float startRadius, float endRadius,分別代表,需要執行的動畫是哪個View,起始位置的X,Y, 開始圓的半徑, 結束圓的半徑!

這邊擴散整個佈局的代碼

    private void show(View v) {
        int w = getResources().getDisplayMetrics().widthPixels;
        int h = getResources().getDisplayMetrics().heightPixels;
        int cx = (v.getLeft() + v.getRight()) / 2;
        int cy = (v.getTop() + v.getBottom()) / 2;
        float r = Math.max(w, h);
        Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
        animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();
    }

效果的邏輯很簡單

A 以SharedElement打開 B ,之後立馬執行CircularReveal擴散整個根佈局, 退出B的時候必須先執行完CircularReveal動畫在關閉頁面。

代碼如下
跳轉代碼

        tv10.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, CircularRevealActivity.class);
                intent.putExtra("CircularReveal", "green");
                ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                        new Pair<View, String>(tv10, ViewCompat.getTransitionName(tv10))
                );
                ActivityCompat.startActivity(activity, intent, compat.toBundle());
            }
        });

執行效果頁面代碼

CircularRevealActivity.java

package com.gjn.testproject;

import android.animation.Animator;
import android.app.SharedElementCallback;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;

import java.util.List;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CircularRevealActivity extends AppCompatActivity {

    View root;
    View v1, v2, v3;
    boolean in = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_circular_reveal);
        in = false;
        root = findViewById(R.id.root_acr);
        v1 = findViewById(R.id.v1);
        v2 = findViewById(R.id.v2);
        v3 = findViewById(R.id.v3);

        String circularReveal = getIntent().getStringExtra("CircularReveal");

        if (circularReveal != null) {
            switch (circularReveal) {
                case "green":
                    root.setBackgroundResource(android.R.color.holo_green_light);
                    break;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                setEnterSharedElementCallback(new SharedElementCallback() {
                    @Override
                    public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
                        if (!in) {
                            in = true;
                            show(v1);
                        }
                    }
                });
            }
        }

        v1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                root.setBackgroundResource(android.R.color.holo_green_light);
                show(v1);
            }
        });

        v2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                root.setBackgroundResource(android.R.color.holo_red_dark);
                show(v2);
            }
        });

        v3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                root.setBackgroundResource(android.R.color.holo_blue_light);
                show(v3);
            }
        });

    }

    @Override
    public void onBackPressed() {
        if (in && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            hide(v1);
        } else {
            super.onBackPressed();
        }
    }

    private void show(View v) {
        int w = getResources().getDisplayMetrics().widthPixels;
        int h = getResources().getDisplayMetrics().heightPixels;
        int cx = (v.getLeft() + v.getRight()) / 2;
        int cy = (v.getTop() + v.getBottom()) / 2;
        float r = Math.max(w, h);
        Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
        animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();
    }

    private void hide(View v) {
        int w = getResources().getDisplayMetrics().widthPixels;
        int h = getResources().getDisplayMetrics().heightPixels;
        int cx = (v.getLeft() + v.getRight()) / 2;
        int cy = (v.getTop() + v.getBottom()) / 2;
        float r = Math.max(w, h);
        Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, r, 0);
        animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();

        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                root.setBackgroundColor(Color.TRANSPARENT);
                finishAfterTransition();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });

    }
}

具體就看代碼吧!其實就是做了兩個動畫而已!


全部代碼

activity_shared_element.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.gjn.testproject.SharedElementActivity">

    <TextView
        android:id="@+id/tv_1_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="24dp"
        android:background="@android:color/holo_blue_bright"
        android:gravity="center"
        android:text="點擊單獨放大"
        android:transitionName="Open"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_2_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="24dp"
        android:background="@android:color/holo_orange_dark"
        android:gravity="center"
        android:text="點擊放大2個"
        android:transitionName="Open2"
        app:layout_constraintStart_toEndOf="@+id/tv_1_ase"
        app:layout_constraintTop_toTopOf="@+id/tv_1_ase" />

    <TextView
        android:id="@+id/tv_3_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginTop="24dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="自定義圖片1"
        android:transitionName="Open3"
        app:layout_constraintStart_toStartOf="@+id/tv_1_ase"
        app:layout_constraintTop_toBottomOf="@+id/tv_1_ase" />

    <TextView
        android:id="@+id/tv_4_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginTop="24dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="自定義圖片2"
        android:transitionName="Open3"
        app:layout_constraintEnd_toEndOf="@+id/tv_2_ase"
        app:layout_constraintTop_toBottomOf="@+id/tv_2_ase" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_ase"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_3_ase" />

    <TextView
        android:id="@+id/tv_5_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="24dp"
        android:gravity="center"
        android:text="Explode"
        app:layout_constraintStart_toEndOf="@+id/tv_2_ase"
        app:layout_constraintTop_toTopOf="@+id/tv_2_ase" />

    <TextView
        android:id="@+id/tv_6_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginStart="24dp"
        android:gravity="center"
        android:text="Slide1"
        app:layout_constraintStart_toEndOf="@+id/tv_5_ase"
        app:layout_constraintTop_toTopOf="@+id/tv_5_ase" />

    <TextView
        android:id="@+id/tv_7_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="Fade"
        app:layout_constraintStart_toStartOf="@+id/tv_5_ase"
        app:layout_constraintTop_toTopOf="@+id/tv_4_ase" />

    <TextView
        android:id="@+id/tv_8_ase"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginTop="24dp"
        android:gravity="center"
        android:text="Slide2"
        app:layout_constraintStart_toStartOf="@+id/tv_6_ase"
        app:layout_constraintTop_toBottomOf="@+id/tv_6_ase" />

    <TextView
        android:id="@+id/tv_9_ase"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:background="@android:color/holo_green_light"
        android:gravity="center"
        android:transitionName="Open3"
        android:text="Slide+SharedElements"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/rv_ase" />

    <TextView
        android:id="@+id/tv_10_ase"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="@android:color/holo_green_light"
        android:gravity="center"
        android:text="CircularReveal1"
        android:transitionName="Open4"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_9_ase" />

</android.support.constraint.ConstraintLayout>

activity_shared.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.gjn.testproject.SharedActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="200dp"
        android:transitionName="Open"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/homepage_banner2" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:transitionName="Open2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        app:srcCompat="@mipmap/homepage_banner1" />

    <ImageView
        android:id="@+id/imageView3"
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:transitionName="Open3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</android.support.constraint.ConstraintLayout>

activity_circular_reveal.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root_acr"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.gjn.testproject.CircularRevealActivity">

    <TextView
        android:id="@+id/v1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="150dp"
        android:transitionName="Open4"
        android:background="@android:color/holo_green_light"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/v2"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginBottom="32dp"
        android:layout_marginEnd="32dp"
        android:background="@android:color/holo_red_dark"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/v3"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginEnd="24dp"
        android:background="@android:color/holo_blue_light"
        app:layout_constraintBottom_toBottomOf="@+id/v2"
        app:layout_constraintEnd_toStartOf="@+id/v2" />


</android.support.constraint.ConstraintLayout>

SharedElementActivity.java

package com.gjn.testproject;

import android.app.Activity;
import android.app.ActivityOptions;
import android.app.SharedElementCallback;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.util.Pair;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.gjn.baserecycleradapterlibrary.BaseRecyclerAdapter;
import com.gjn.baserecycleradapterlibrary.RecyclerViewHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class SharedElementActivity extends AppCompatActivity {

    TextView tv1, tv2, tv3, tv4, tv5, tv6, tv7, tv8, tv9, tv10;

    Activity activity;

    String[] imgs = {"https://ws1.sinaimg.cn/large/0065oQSqgy1fxd7vcz86nj30qo0ybqc1.jpg",
            "https://ws1.sinaimg.cn/large/0065oQSqgy1fwyf0wr8hhj30ie0nhq6p.jpg",
            "https://ws1.sinaimg.cn/large/0065oQSqgy1fwgzx8n1syj30sg15h7ew.jpg",
            "https://ws1.sinaimg.cn/large/0065oQSqly1fw8wzdua6rj30sg0yc7gp.jpg",
            "https://ws1.sinaimg.cn/large/0065oQSqly1fuh5fsvlqcj30sg10onjk.jpg",
            "https://ws1.sinaimg.cn/large/0065oQSqly1fubd0blrbuj30ia0qp0yi.jpg"};
    Bundle bundle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        //設置允許通過ActivityOptions.makeSceneTransitionAnimation發送或者接收Bundle
//        getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
//        //設置使用TransistionManager進行動畫,不設置的話系統會使用一個默認的TransitionManager
//        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        setContentView(R.layout.activity_shared_element);

        activity = this;

        tv1 = findViewById(R.id.tv_1_ase);
        tv2 = findViewById(R.id.tv_2_ase);
        tv3 = findViewById(R.id.tv_3_ase);
        tv4 = findViewById(R.id.tv_4_ase);
        tv5 = findViewById(R.id.tv_5_ase);
        tv6 = findViewById(R.id.tv_6_ase);
        tv7 = findViewById(R.id.tv_7_ase);
        tv8 = findViewById(R.id.tv_8_ase);
        tv9 = findViewById(R.id.tv_9_ase);
        tv10 = findViewById(R.id.tv_10_ase);
        RecyclerView RV = findViewById(R.id.rv_ase);

        List<String> list = new ArrayList<>();

        list.add("圖片1");
        list.add("圖片2");
        list.add("圖片3");
        list.add("圖片4");
        list.add("圖片5");
        list.add("圖片6");

        RV.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
        BaseRecyclerAdapter<String> adapter = new BaseRecyclerAdapter<String>(activity, R.layout.item_ase, list) {
            @Override
            public void bindData(RecyclerViewHolder holder, String s, int i) {
                holder.setTextViewText(R.id.tv_ia, s);
            }
        };
        RV.setAdapter(adapter);

        adapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int i) {
                TextView textView = view.findViewById(R.id.tv_ia);
                ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                        new Pair<View, String>(textView, ViewCompat.getTransitionName(textView))
                );
                Intent intent = new Intent(activity, SharedActivity.class);
                intent.putExtra("imgurl", imgs[i]);
                ActivityCompat.startActivity(activity, intent, compat.toBundle());
            }
        });


        setClick();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            setExitSharedElementCallback(new SharedElementCallback() {
                @Override
                public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
                    if (bundle != null) {
                        int id = bundle.getInt("imgId");
                        String url = bundle.getString("imgUrl");
                        Log.e("-s-", "id = " + id);
                        Log.e("-s-", "url = " + url);
                        bundle = null;
                    }
                }
            });
        }
    }

    @Override
    public void onActivityReenter(int resultCode, Intent data) {
        Log.e("-s-", "resultCode = " + resultCode);
        if (resultCode == 101) {
            bundle = new Bundle(data.getExtras());
        }
    }

    private void setClick() {

        tv1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, SharedActivity.class);
                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity, tv1,
                        ViewCompat.getTransitionName(tv1)).toBundle());
            }
        });

        tv2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Pair<View, String> p1 = new Pair<View, String>(tv1, ViewCompat.getTransitionName(tv1));
                Pair<View, String> p2 = new Pair<View, String>(tv2, ViewCompat.getTransitionName(tv2));
                ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, p1, p2);
                Intent intent = new Intent(activity, SharedActivity.class);
                ActivityCompat.startActivity(activity, intent, compat.toBundle());
            }
        });

        tv3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                        new Pair<View, String>(tv3, ViewCompat.getTransitionName(tv3))
                );
                Intent intent = new Intent(activity, SharedActivity.class);
                intent.putExtra("imgid", R.mipmap.ic_launcher_round);
                ActivityCompat.startActivity(activity, intent, compat.toBundle());
            }
        });

        tv4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                        new Pair<View, String>(tv4, ViewCompat.getTransitionName(tv4))
                );
                Intent intent = new Intent(activity, SharedActivity.class);
                intent.putExtra("imgid", R.mipmap.ic_launcher);
                ActivityCompat.startActivity(activity, intent, compat.toBundle());
            }
        });

        tv5.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, TransitionActivity.class);
                intent.putExtra("transition", "Explode");
                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
            }
        });

        tv6.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, TransitionActivity.class);
                intent.putExtra("transition", "Slide");
                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
            }
        });

        tv7.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, TransitionActivity.class);
                intent.putExtra("transition", "Fade");
                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
            }
        });

        tv8.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, TransitionActivity.class);
                intent.putExtra("transition", "Slide2");
                startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle());
            }
        });

        tv9.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, SharedActivity.class);
                intent.putExtra("transition", "Slide+");
                intent.putExtra("imgurl", imgs[0]);
                ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                        new Pair<View, String>(tv9, ViewCompat.getTransitionName(tv9))
                );
                ActivityCompat.startActivity(activity, intent, compat.toBundle());
            }
        });

        tv10.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(activity, CircularRevealActivity.class);
                intent.putExtra("CircularReveal", "green");
                ActivityOptionsCompat compat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity,
                        new Pair<View, String>(tv10, ViewCompat.getTransitionName(tv10))
                );
                ActivityCompat.startActivity(activity, intent, compat.toBundle());
            }
        });

    }
}

TransitionActivity.java

package com.gjn.testproject;

import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.transition.Explode;
import android.transition.Fade;
import android.transition.Slide;
import android.transition.Transition;
import android.view.Gravity;
import android.view.animation.BounceInterpolator;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class TransitionActivity extends AppCompatActivity {

    private String transition;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transition);

        transition = getIntent().getStringExtra("transition");

        if (transition != null) {
            switch (transition) {
                case "Explode":
                    Explode explode = new Explode();
                    setDuration(explode);
                    getWindow().setEnterTransition(explode);
                    break;
                case "Slide":
                    Slide slide = new Slide();
                    slide.setSlideEdge(Gravity.RIGHT);
                    setDuration(slide);
                    getWindow().setEnterTransition(slide);
                    break;
                case "Slide2":
                    Slide slide2 = new Slide();
                    slide2.setSlideEdge(Gravity.RIGHT);
                    setDuration(slide2);
                    //回彈效果
                    slide2.setInterpolator(new BounceInterpolator());
                    getWindow().setEnterTransition(slide2);
                    break;
                case "Fade":
                    Fade fade = new Fade();
                    setDuration(fade);
                    getWindow().setEnterTransition(fade);
                    break;
            }
        }
    }

    private void setDuration(Transition transition) {
        transition.setDuration(800);
    }

    @Override
    public void onBackPressed() {
        if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // 如果沒有 return transition 被定義,將使用反進入的動畫
            switch (transition) {
//                case "Explode":
//                    Explode explode = new Explode();
//                    getWindow().setReturnTransition(explode);
//                    break;
//                case "Slide":
//                    Slide slide = new Slide();
//                    slide.setSlideEdge(Gravity.RIGHT);
//                    getWindow().setReturnTransition(slide);
//                    break;
                case "Slide2":
//                    Slide slide2 = new Slide();
//                    slide2.setSlideEdge(Gravity.RIGHT);
//                    //回彈效果
//                    slide2.setInterpolator(new BounceInterpolator());
//                    getWindow().setReturnTransition(slide2);
                    Slide slide = new Slide();
                    slide.setSlideEdge(Gravity.LEFT);
                    setDuration(slide);
                    getWindow().setReturnTransition(slide);
                    break;
//                case "Fade":
//                    Fade fade = new Fade();
//                    getWindow().setReturnTransition(fade);
//                    break;
            }
            //一定要調用finishAfterTransition
            finishAfterTransition();
        } else {
            super.onBackPressed();
        }
    }
}

SharedActivity.java

package com.gjn.testproject;

import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.transition.Slide;
import android.view.Gravity;
import android.widget.ImageView;

import com.bumptech.glide.Glide;

public class SharedActivity extends AppCompatActivity {

    int imgId;
    String imgUrl;
    String transition;
    ImageView img3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shared);

        img3 = findViewById(R.id.imageView3);

        imgId = getIntent().getIntExtra("imgid", -1);
        imgUrl = getIntent().getStringExtra("imgurl");

        transition = getIntent().getStringExtra("transition");
        if (transition != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            switch (transition) {
                case "Slide+":
                    Slide slide = new Slide();
                    slide.setSlideEdge(Gravity.RIGHT);
                    getWindow().setEnterTransition(slide);
                    break;
            }
        }
        if (imgUrl != null) {
            Glide.with(this).load(imgUrl).into(img3);
        } else if (imgId != -1) {
            Glide.with(this).load(imgId).into(img3);
        }

    }

    @Override
    public void finishAfterTransition() {
        Intent data = new Intent();
        data.putExtra("imgId", imgId);
        data.putExtra("imgUrl", imgUrl);
        setResult(101, data);
        super.finishAfterTransition();
    }
}

CircularRevealActivity.java

package com.gjn.testproject;

import android.animation.Animator;
import android.app.SharedElementCallback;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AccelerateDecelerateInterpolator;

import java.util.List;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CircularRevealActivity extends AppCompatActivity {

    View root;
    View v1, v2, v3;
    boolean in = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_circular_reveal);
        in = false;
        root = findViewById(R.id.root_acr);
        v1 = findViewById(R.id.v1);
        v2 = findViewById(R.id.v2);
        v3 = findViewById(R.id.v3);

        String circularReveal = getIntent().getStringExtra("CircularReveal");

        if (circularReveal != null) {
            switch (circularReveal) {
                case "green":
                    root.setBackgroundResource(android.R.color.holo_green_light);
                    break;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                setEnterSharedElementCallback(new SharedElementCallback() {
                    @Override
                    public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
                        if (!in) {
                            in = true;
                            show(v1);
                        }
                    }
                });
            }
        }

        v1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                root.setBackgroundResource(android.R.color.holo_green_light);
                show(v1);
            }
        });

        v2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                root.setBackgroundResource(android.R.color.holo_red_dark);
                show(v2);
            }
        });

        v3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                root.setBackgroundResource(android.R.color.holo_blue_light);
                show(v3);
            }
        });

    }

    @Override
    public void onBackPressed() {
        if (in && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            hide(v1);
        } else {
            super.onBackPressed();
        }
    }

    private void show(View v) {
        int w = getResources().getDisplayMetrics().widthPixels;
        int h = getResources().getDisplayMetrics().heightPixels;
        int cx = (v.getLeft() + v.getRight()) / 2;
        int cy = (v.getTop() + v.getBottom()) / 2;
        float r = Math.max(w, h);
        Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, r);
        animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();
    }

    private void hide(View v) {
        int w = getResources().getDisplayMetrics().widthPixels;
        int h = getResources().getDisplayMetrics().heightPixels;
        int cx = (v.getLeft() + v.getRight()) / 2;
        int cy = (v.getTop() + v.getBottom()) / 2;
        float r = Math.max(w, h);
        Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, r, 0);
        animator.setDuration(500).setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();

        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                root.setBackgroundColor(Color.TRANSPARENT);
                finishAfterTransition();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });

    }
}

資料

【Transition】Android炫酷的Activity切換效果,共享元素
Activity間的跳轉動畫—Transition
使用Circular Reveal爲你的應用添加揭露動畫效果

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