一次在逛Github的時候,看到一個漂亮的登陸界面,用的是Transition作的。我就直接貼上地址:html
MaterialLoginandroid
固然,若是單純的直接拿過來用,沒有任何意義。主要仍是來看具體如何實現的。我就來寫下具體如何一步步的來實現這個效果。git
我也按照相應的原理寫了個Demo。最後的效果以下圖所示(其中layout佈局我就直接從github上面拷貝過來了):github
首先咱們來看下什麼是Transition。你們看仔細是Transition,而不是Translate。咱們直接看翻譯:api
而Translate一般咱們指的是平移的動畫操做。bash
因此咱們知道了用的是過渡的方式來作,那什麼是過渡呢?app
Android對於開發者提供了愈來愈多的動畫API支持。從API 1就存在的Drawable Animation和View Animation,以及API 11(Android 3.0)之後加入的Property Animation。而過渡動畫Transition是在API 19(Android 4.4.2)中加入的。ide
基礎知識我就不說了,直接看其餘文章傳送門:佈局
Android 過渡(Transition)動畫解析之基礎篇測試
因此初步咱們能夠理解爲(可能這麼說明有不對,能夠提出):
場景(scenes)和變換(transitions)。場景(scenes)定義了當前的UI狀態,變換(transitions)則定義了在不一樣場景之間動畫變化的過程。
當一個場景改變的時候,transition主要負責:
(1)捕捉每一個View在開始場景和結束場景時的狀態。
(2)根據兩個場景(開始和結束)之間的區別建立一個Animator。
Android 5.0中Transition能夠被用來實現Activity或者Fragment切換時的異常複雜的動畫效果。
雖然在之前的版本中,已經可使用Activity的overridePendingTransition() 和 FragmentTransaction的setCustomAnimation()來實現Activity或者Fragment的動畫切換,可是他們僅僅侷限與將整個視圖一塊兒動畫變換。新的Lollipop api更進了一步,讓單獨的view也能夠在進入或者退出其佈局容器中時發生動畫效果,甚至還能夠在不一樣的activity/Fragment中共享一個view。
仍是上面那個圖,只是變成了二個Activity界面:
咱們在跳轉到第二個Activity的時候,咱們會有個過場動畫。會第一個Activity的按鈕移動到第二個Activity的按鈕。效果以下所示:
因此咱們再回頭看下面這種效果,是否是就知道怎麼實現了,用的是Activity的過渡動畫了。
你們也能夠看看下面的相關文章連接:
Activity和Fragment Transition介紹
深刻理解共享元素變換(Shared Element Transition)-上
咱們先準備第一個Activity,界面以下:
咱們讓那個按鈕"+"能移動到頂部:
咱們由前面的demo說明已經知道了,啓動第二個Activity,咱們咱們先讓第二個Activity的界面以下所示:
咱們設置第二個Activity的主題爲:
<style name="Translucent" parent="Theme.AppCompat.Light.NoActionBar">
//第一個activity的狀態欄顏色爲#0288D1
<item name="colorPrimaryDark">#0288D1</item>
//第二個activity的背景爲透明,
//這樣能夠看獲得第一個Activity的界面
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
複製代碼
沒錯,咱們在第二個界面先寫上一個按鈕"X",這樣咱們啓動第二個Activity的時候就蓋在了第一個Activity的上面,同時這個fab按鈕也有了動畫的效果:
代碼很簡單,只要讓第一個Activity的按鈕的android:transitionName
與第二個Activity的按鈕的android:transitionName
同樣就能夠。咱們稱這個爲共享元素。
FloatingActionButton btn = findViewById(R.id.fab);;
ActivityOptionsCompat optionsCompat
= ActivityOptionsCompat.makeSceneTransitionAnimation(LoginMainActivity.this,btn,btn.getTransitionName());
startActivity(new Intent(LoginMainActivity.this,RegisterMainActivity.class),optionsCompat.toBundle());
複製代碼
而後經過ActivityOptionsCompat
來記錄當前這個Activity的這個fab按鈕的狀態。而後在startActivity
的時候,經過optionsCompat.toBundle()
把內容帶到了第二個Activity
中。第二個Activity就會讓如今的相同trasitionName
的fab按鈕,以傳過來的第一個Activity
的按鈕相同位置的爲起始點,而後經過動畫到了最終的地方。(因此動畫是在第二個Activity中完成的,只是按鈕的起始狀態是以第一個Activity傳過來的按鈕的狀態信息相同,而後到最終用戶設置的位置。)
咱們能夠看到,共享元素變換並非真正實現了兩個activity或者Fragment之間元素的共享,實際上咱們看到的幾乎全部變換效果中(不論是B進入仍是B返回A),共享元素都是在B中繪製出來的。Framework沒有真正試圖將A中的某個元素傳遞給B,而是採用了不一樣的方法來達到相同的視覺效果。A傳遞給B的是共享元素的狀態信息。B利用這些信息來初始化共享View元素,讓它們的位置、大小、外觀與在A中的時候徹底一致。當變換開始的時候,B中除了共享元素以外,全部的其餘元素都是不可見的。隨着動畫的進行,framework 逐漸將B的activity窗口顯示出來,當動畫完成,B的窗口才徹底可見。
而且其實動畫是繪製在ViewOverlay上面,能夠看看這篇文章:ViewOverlay與animation介紹
咱們直接不作任何處理,默認是fab按鈕的位置變化是直線。 咱們更但願是:
咱們能夠設置共享元素的進入動畫:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/linear_out_slow_in"
android:duration="3000">
<changeBounds>
<arcMotion
android:maximumAngle="0"
android:minimumHorizontalAngle="60"
android:minimumVerticalAngle="90" />
</changeBounds>
</transitionSet>
複製代碼
//獲取過渡動畫
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.fabtransition);
設置共享元素的進入動畫
getWindow().setSharedElementEnterTransition(transition);
複製代碼
這裏使用的是arcMotion來作的曲線路徑.
裏面的介紹我用的谷歌翻譯翻譯的,大體應該是這個意思: PathMotion在包含兩個點的假想圓上沿圓弧生成曲線路徑。 若是點之間的水平距離小於垂直距離,則圓的中心點將與終點水平對齊。 若是垂直距離小於水平距離,則圓的中心點將與終點垂直對齊。 當兩點接近水平或垂直時,運動的曲線將會變小,由於圓的中心距兩點都很遠。 要強制路徑的曲率,可使用setMinimumHorizontalAngle(float)和setMinimumVerticalAngle(float)來設置兩點之間的弧的最小角度。
其餘參考文章:
咱們上一步對fab按鈕設置了過渡的動畫。咱們能夠對這個過渡動畫設置結束的監聽,而後其餘咱們的註冊界面的出現:
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.fabtransition);
getWindow().setSharedElementEnterTransition(transition);
transition.addListener(new Transition.TransitionListener() {
.....
.....
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
/*咱們能夠在動畫結束後,
能夠在這裏寫上代碼,
讓註冊界面出現
*/
.....
.....
}
});
複製代碼
這是咱們的第二個Activity的佈局變成了這樣:
只不過默認這個註冊界面是不可見的,等到咱們的fab按鈕動畫結束後,咱們再讓註冊界面可見就能夠了。
這裏咱們能夠直接在上面fab按鈕動畫結束的時候,直接讓註冊界面出現(由於這個註冊界面是用CardView寫的,因此這裏直接用cardView來指這個實例),咱們能夠在上面的結束監聽裏面直接設置:
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
//設置可見
cardView.setVisibility(View.VISIBLE);
}
複製代碼
效果以下:
咱們發現,直接忽然出現,雖然功能實現了,但咱們仍是但願有更好看的效果,就像文章開頭那樣,這個註冊界面是慢慢展開的。因此咱們在fab按鈕過渡動畫結束後,不是簡單的對cardView設置View.VISIBLE就能夠。咱們使用揭露動畫來實現:
Animator mAnimator = ViewAnimationUtils.createCircularReveal(cardView,cardView.getWidth()/2
,0,0,cardView.getHeight());
mAnimator.setDuration(500);
mAnimator.setInterpolator(new AccelerateInterpolator());
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
cardView.setVisibility(View.VISIBLE);
}
});
mAnimator.start();
複製代碼
揭露動畫參考文章:
使用Circular Reveal爲你的應用添加揭露動畫效果
因此咱們這麼使用後效果變成了:
這裏有二種方式:
咱們的fab鍵從左邊移動到了上邊,而後若是你按返回鍵,你會發現自動fab鍵會先執行相應的自動回去動畫,而後activity再關閉。好比你直接對fab鍵設置了點擊事件:
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
複製代碼
直接調用finish()方法的話,你就會發現,沒有fab鍵返回的動畫,而是直接第二個activity關閉,顯示第一個activity的見面。這樣很不友善。
咱們知道默認按返回鍵是調用了:
@Override
public void onBackPressed() {
super.onBackPressed();
}
複製代碼
說明調用onBackPressed
會調用退出動畫效果後再finish()
;
參考文章:
最經常使用的Activity的onBackPressed()與finish()的區別.
因此咱們知道了,咱們點擊fab鍵返回的時候不能直接finish,而是最後一步是調用super.onBackPressed();
。
因此咱們最終是先讓註冊界面慢慢消失,消失後調用super.onBackPressed();
。
//覆寫返回鍵操做,
//執行註冊界面消失動畫,
//而後再執行super.onBackPressed();
@Override
public void onBackPressed() {
animateRevealClose();
}
//fab的點擊事件與上面同樣
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
animateRevealClose();
}
});
//註冊界面界面慢慢消失,而後調用super.onBackPressed()
//而後fab鍵會執行動畫回到原始位置,而後第二個Activity關閉。
//而後顯示了第一個Activity
public void animateRevealClose(){
Animator mAnimator = ViewAnimationUtils.createCircularReveal(cardView,cardView.getWidth()/2
,0,cardView.getHeight(),0);
mAnimator.setDuration(500);
mAnimator.setInterpolator(new AccelerateInterpolator());
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
cardView.setVisibility(View.GONE);
btn.setImageResource(R.drawable.plus);
RegisterMainActivity.super.onBackPressed();
}
});
mAnimator.start();
}
複製代碼
最後實現就是這樣子了:
哪裏錯誤了,你們留言回覆哦,多謝支持。o( ̄︶ ̄)o
大佬若是能幫我解答下下面二個問題,很是感謝:
我在使用arcMotion的時候,小米5(6.0)與華爲(7.0),呈現的曲線效果差異很大,(gif圖是小米的,因此fab鍵移動的時候更像是直線,可是華爲就很明顯的是曲線)不知道是什麼緣由,知道的能夠告訴我下。