天天看點

Android淺談轉場效果

Android淺談轉場效果

前言

又沒什麼好的思路,還是随便寫一些,是以這次就來整點活。

我們都知道Activity的跳轉擁有預設的跳轉動畫,或者把這個預設的動畫給取消,就會讓跳轉的效果讓人覺得比較生硬。那我們能不能做出一些比較好的轉場效果呢?本篇隻介紹實作的思路,而不去深究某個思路的具體實作,因為有些知識點内容太多了,如果深入去看怕是很難一時半會講明白。

Activity跳轉動畫

Activity是可以設定跳轉動畫的,有了動畫之後,跳轉的效果的體驗就會比之前好一些。

我這裡先寫兩個動畫,跳轉時新頁面進入的動畫

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="100%"
        android:toXDelta="0%"
        android:fromYDelta="0%"
        android:toYDelta="0%"
        android:duration="1000"/>
</set>
複制代碼           

和舊頁面退出的動畫

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="0%"
        android:toXDelta="-100%"
        android:fromYDelta="0%"
        android:toYDelta="0%"
        android:duration="1000"/>
</set>
複制代碼           

然後在跳轉時調用方法

Intent intent = new Intent(TwoActivity.this, ThreeActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.activity_start,R.anim.activity_end);
複制代碼           

這樣就能實作頁面切換時的動畫效果,但是要有一點需要注意,這個動畫一定要合理,因為是動畫展示完之後第二個頁面才展示,如果你動畫的邏輯涉及得不合理,會出現動畫結束之後再展示第二個頁面這個過程會顯得很生硬。

但是如果僅僅隻有頁面切換的動畫,還是覺得差點意思。這時候就需要發揮自己想象力了。我這裡可以用一個自定義View和這個動畫進行關聯,讓他們看起來是一個整體的效果。

比如我這樣寫一個View

<RelativeLayout
    android:id="@+id/btn"
    android:layout_width="120dp"
    android:layout_height="60dp"
    android:background="@drawable/test_start"
    android:layout_alignParentEnd="true"
    android:layout_marginTop="50dp"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下一步"
        android:layout_centerInParent="true"
        android:textColor="#ffff"
        />
</RelativeLayout>
複制代碼           

設定一個背景,顔色和第二個頁面的顔色相同

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <!--背景顔色-->
    <solid android:color="@color/jh_blue" />
    <corners
        android:topLeftRadius="100dp"
        android:bottomLeftRadius="100dp"
        />
</shape>
複制代碼           

最終會展現出這樣的效果

Android淺談轉場效果

共享元素

在5.0之後,android提供了共享元素的實作。Material Design剛出來的時候我還沒畢業,我覺得現在回去看它的這個思想和一些内容,确實能有一些其它的收獲,這個概念的提出确實牛逼。

共享元素簡單來說效果就是你兩個頁面之間的跳轉,設定了共享的元素會做個動畫,會讓人感覺是這個頁面的View做了一個動畫移動到另一個頁面,展現出現的效果就很好。

那既然效果好,為什麼不普遍使用呢?我覺得有兩個原因,第一個是之前不像現在一樣基本都是5.0以上的手機,之前要對4.4做适配,然後開發中,沒那方面的需求,自然也不會往那個方向去想,甚至都不知道Android有提供這個東西。第二個原因是好用是好用,相對的坑也多。

可以先看看如何實作,我這裡寫個簡單點的Demo,首先在第一個頁面建立一個view

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    >

    <ImageView
        android:id="@+id/iv_test"
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:layout_marginTop="500dp"
        android:src="@drawable/aaaaa"
        android:transitionName="test"
        />

</RelativeLayout>
複制代碼           

看到這裡有個屬性transitionName,這個就是定義共享元素,然後在第二個頁面也建立一個view

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/blue"
    android:gravity="center_horizontal"
    >

    <ImageView
        android:id="@+id/iv_test"
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:layout_marginTop="20dp"
        android:src="@drawable/aaaaa"
        android:transitionName="test"
        />

</RelativeLayout>
複制代碼           

看到他們的transitionName是一樣的,然後在跳轉時用ActivityOptions.makeSceneTransitionAnimation就行了,具體的它已經裡面幫你封裝好了

ImageView imageView = findViewById(R.id.iv_test);
imageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(TwoActivity.this, ThreeActivity.class);
        Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(TwoActivity.this, imageView, "test")
                .toBundle();
        startActivity(intent, bundle);
    }
});
複制代碼           

就這麼簡單,其中要注意的是你頁面的style如果是用Material的style就沒什麼問題,但如果不是,你就需要在你的style中設定

<item name="android:windowActivityTransitions">true</item>
複制代碼           

當然也可以在代碼中動态設定

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
複制代碼           

最終的效果就是這樣的

Android淺談轉場效果

這個動畫效果是預設的,如果你要自己實作,就需要自己去寫transition,比如我這裡這樣設定時間

<fade xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    />
複制代碼           
<style name="test_style" parent="android:Theme.NoTitleBar.Fullscreen">
    <item name="android:windowActivityTransitions">true</item>
    <item name="android:windowEnterTransition">@transition/activity_fade</item>
    <item name="android:windowExitTransition">@transition/activity_fade</item>
</style>
複制代碼           

它其實功能很龐大,同樣的坑也會很多。我這裡就不往下說了,前面也說了,這篇文章主要是寫個思路,你要是能把它玩好了,就和自定義view一樣,屬性動畫一樣,想實作什麼效果,就實作什麼效果,我也不常用,對這個技術點也隻是入門。

通過window去實作

這個思路是在activity上方有個圖層,圖層中有個view和下方圖層的view的大小和位置相同,然後去改上圖層的view。此時如果下圖層做跳轉,也不會影響,就是能實作和上面共享元素一樣的效果。但是我以前用的window是系統級别的window,是以能實作效果,其它級别的不知道會不會有問題,得開發時候具體調試才知道,總之主要看思路,先不用在意細節。

還是用一個Demo來舉例,先寫Activity的頁面,兩個textview是用做辨別看效果的

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    >

    <ImageView
        android:id="@+id/iv_test"
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:layout_marginTop="500dp"
        android:src="@drawable/aaaaa"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="在圖檔上方"
        android:layout_marginTop="480dp"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="在圖檔下方"
        android:layout_below="@+id/iv_test"
        />

</RelativeLayout>
複制代碼           

再寫window的布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    >

    <ImageView
        android:id="@+id/iv_test"
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:layout_marginTop="500dp"
        android:src="@drawable/aaaaa"
        />

</RelativeLayout>
複制代碼           

可以看到兩個view的初始位置和大小都相同,然後看看具體的實作

public class TwoActivity extends Activity {

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

        ImageView imageView = findViewById(R.id.iv_test);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                playWindow();
            }
        });
    }

    private void playWindow() {
        RelativeLayout relativeLayout = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.test_window, null);
        View windowView = relativeLayout.findViewById(R.id.iv_test);
        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams wlp = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_APPLICATION,
                WindowManager.LayoutParams.FLAG_FULLSCREEN
                , PixelFormat.RGBA_8888);
        windowManager.addView(relativeLayout, wlp);
        windowView.post(new Runnable() {
            @Override
            public void run() {
                start(windowView);
            }
        });
    }

    private void start(final View view) {
        int w = view.getWidth();
        int h = view.getHeight();
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 300);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int v = (int) animation.getAnimatedValue();
                RelativeLayout.LayoutParams rlp = (RelativeLayout.LayoutParams) view.getLayoutParams();
                rlp.width = w + v;
                rlp.height = h + v;
                view.setLayoutParams(rlp);
                view.invalidate();
            }
        });
        valueAnimator.setDuration(700).start();
    }

}
複制代碼           

demo裡面我為了友善用了TYPE_APPLICATION,但實際我這邊開發用的是FIRST_SYSTEM_WINDOW以上的系統層級彈窗。

然後我們來看看最終的效果

Android淺談轉場效果

這裡是做了個放大的效果,但實際你可以放大後跳轉Activity然後移動位置,再縮小,就有無縫轉場的效果,其實和上面的共享元素的效果類似。

總結

能實作轉場的方式很多,不用拘束于調用原生的方法,比如如果真依賴原生提供的方法,要是真要适配5.0以下的怎麼做?最重要的是要發揮自己的想象力,去思考。當然,這個原生提供的系統确實也有很多學問在裡邊,想要玩得爐火純青,也是需要不斷的去嘗試,不斷的去踩坑。

繼續閱讀