這篇文章的套路如下,帶你裝逼帶你飛。
- 概念
- 如何使用
- 設置 相同的名稱
- 啟動 startActivity
- Fragment to Fragment
- 設置多組元素
- 自定義過度動畫
- 源碼解讀
- 如何兼容低版本
先看一下 效果吧!
1.概念
在5.0之前,不同活動或片段的過渡是進入和退出動畫,視圖層次結構彼此獨立的轉換。
過渡動畫則是:把兩個activity當中的相同元素關聯起來做連貫的動畫; 從而達到不同視圖之間的元素關聯達到 酷炫、爆炸等酷炫而又優雅地效果。
引導用戶視覺。
共享元素類型:
explode:從場景的中心移入或移出
slide:從場景的邊緣移入或移出
fade:調整透明度產生漸變效果
動畫效果:
changeBounds - 改變目標視圖的布局邊界
changeClipBounds - 裁剪目標視圖邊界
changeTransform - 改變目標視圖的縮放比例和旋轉角度
changeImageTransform - 改變目標圖片的大小和縮放比例
2. 如何使用
2.1 設置主題
先要在系統主題中設置主題,或者在java代碼中設置支持過渡動畫的主題,我們在頁面切換時才有過渡動畫的效果,有以下兩種方式:
2.1.1 方法一
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
// 設置一個exit transition
getWindow().setExitTransition(new Explode());
可以通過如下方法在代碼總設置進入與退出時 Transition 效果:
- Window.setEnterTransition():普通transition的進入效果
- Window.setExitTransition():普通transition的退出效果
- Window.setSharedElementEnterTransition():共享元素transition的進入效果
- Window.setSharedElementExitTransition():共享元素transition的退出效果
2.1.2 方法二
在xml中設置 默認配置
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:windowContentTransitions">true</item>
<!-- 指定進入和退出transitions -->
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>
<!-- 指定shared element transitions -->
<item name="android:windowSharedElementEnterTransition">
@transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">
@transition/change_image_transform</item>
...
</style>
2.2 設置 相同的名稱
共享元素切換,首先要保證兩個頁面內view的transitionName
是一致的
設置:android:transitionName
如下:
MainActivity.xml
<android.support.v7.widget.CardView
...>
<ImageView
android:id="@+id/ivProfile"
android:transitionName="profile"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="160dp" />
...
</android.support.v7.widget.CardView>
DetailActivity.xml
<LinearLayout
...>
<ImageView
android:id="@+id/ivProfile"
android:transitionName="profile"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="380dp" />
...
</LinearLayout>
2.3 啟動 startActivity
第一個Acitivty執行頁面跳轉時需要加上 ActivityOptionsCompat.toBundle()
才會顯示出過渡動畫的效果
Intent intent = new Intent(this, DetailsActivity.class);
// Pass data object in the bundle and populate details activity.
intent.putExtra(DetailsActivity.EXTRA_CONTACT, contact);
//
getWindow().setExitTransition(new Explode()); //設置頁面切換效果
//第一次進入時使用
getWindow().setEnterTransition(new explode);
//再次進入時使用
getWindow().setReenterTransition(new explode);
Transition ts = new ChangeClipBounds(); //設置元素動畫
ts.setDuration(3000);
getWindow().setSharedElementExitTransition(ts);
ActivityOptionsCompat options = ActivityOptionsCompat.
makeSceneTransitionAnimation(this, (View)ivProfile, "profile");
startActivity(intent, options.toBundle());
從第二個Activity反轉場景轉換動畫,請調用Activity.supportFinishAfterTransition()而不是Activity.finish(),如果你使用了toolbar的返回按鈕行為,也需要去覆蓋它
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
supportFinishAfterTransition();
return true;
}
return super.onOptionsItemSelected(item);
}
2.4 Fragment to Freagment
// Get access to or create instances to each fragment
FirstFragment fragmentOne = ...;
SecondFragment fragmentTwo = ...;
// Check that the device is running lollipop
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Inflate transitions to apply
Transition changeTransform = TransitionInflater.from(this).
inflateTransition(R.transition.change_image_transform);
Transition explodeTransform = TransitionInflater.from(this).
inflateTransition(android.R.transition.explode);
// Setup exit transition on first fragment
fragmentOne.setSharedElementReturnTransition(changeTransform);
fragmentOne.setExitTransition(explodeTransform);
// Setup enter transition on second fragment
fragmentTwo.setSharedElementEnterTransition(changeTransform);
fragmentTwo.setEnterTransition(explodeTransform);
// Find the shared element (in Fragment A)
ImageView ivProfile = (ImageView) findViewById(R.id.ivProfile);
// Add second fragment by replacing first
FragmentTransaction ft = getFragmentManager().beginTransaction()
.replace(R.id.container, fragmentTwo)
.addToBackStack("transaction")
.addSharedElement(ivProfile, "profile");
// Apply the transaction
ft.commit();
}
else {
// Code to run on older devices
}
2.6 設置多組元素
如最開始的展示效果,可以知道,我們是可以設置多組元素的關聯并且對每個元素可以執行不同的過渡動畫的,它的方式如下:
Intent intent = new Intent(context, DetailsActivity.class);
intent.putExtra(DetailsActivity.EXTRA_CONTACT, contact);
Pair<View, String> p1 = Pair.create((View)ivProfile, "profile");
Pair<View, String> p2 = Pair.create(vPalette, "palette");
Pair<View, String> p3 = Pair.create((View)tvName, "text");
ActivityOptionsCompat options = ActivityOptionsCompat.
makeSceneTransitionAnimation(this, p1, p2, p3);
startActivity(intent, options.toBundle());
注意:默認情況下android.util.Pair將被導入,但是我們要選擇android.support.v4.util.Pair類。
這里要避免元素過多導致分散注意力的動畫,這樣可能會失去過渡動畫的意義。
3.自定義過度動畫
本質上就是重寫 Transition;
套路如下:
1、繼承 Visibility 或者 Transition
2、自定義動畫:實現 VIsibility/Transition內方法
3、在頁面中將自定義Transition設入:getWindow().setEnterTransition
或者setSharedElementEnterTransition
看一下自定義的效果
3.1 一些概念:
- Visibility extends Transition 頁面切換轉場動畫類;
- shareView 的移動的軌跡路徑PathMotion類
ArcMotion arcMotion = new ArcMotion();
arcMotion.setMinimumHorizontalAngle(50f);
arcMotion.setMinimumVerticalAngle(50f);
3.2 自定義動畫
繼承 Visibility:
public void captureStartValues(TransitionValues transitionValues) 這里保存計算動畫初始狀態的一個屬性值
public void captureEndValues(TransitionValues transitionValues) 這里保存計算動畫結束狀態的一個屬性值
public Animator onAppear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)
如果是進入動畫 即顯示某個 View 則會執行這個方法
public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)
如果是退出 , VIew 則會執行這個方法
繼承 Transition:
@Override
public void captureStartValues(TransitionValues transitionValues) {
//初始值保存
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
//結束值保存
}
createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues){
//創建執行動畫
}
3.3 調用
getWindow().setEnterTransition(new CommentEnterTransition(this, toolbar, bottom_aty_love));//設置
getWindow().setSharedElementEnterTransition(buildShareElemEnterSet());//設置進入轉換動畫
getWindow().setSharedElementReturnTransition(buildShareElemReturnSet());//設置退出轉換動畫
自定義過渡動畫的Activity:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/activity/LoveActivity.java
4. 源碼解讀
帶著幾個疑問:
- Transition內方法在哪里被調用
- 看一個 系統 的 轉換類寫法 ,這邊選擇
Explode
- 在
TransitionKitKat
內被調用
@Override
public void captureEndValues(TransitionValues transitionValues) {
android.transition.TransitionValues internalValues =
new android.transition.TransitionValues();
copyValues(transitionValues, internalValues);
mTransition.captureEndValues(internalValues);
copyValues(internalValues, transitionValues);
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
android.transition.TransitionValues internalValues =
new android.transition.TransitionValues();
copyValues(transitionValues, internalValues);
mTransition.captureStartValues(internalValues);
copyValues(internalValues, transitionValues);
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
android.transition.TransitionValues internalStartValues;
android.transition.TransitionValues internalEndValues;
if (startValues != null) {
internalStartValues = new android.transition.TransitionValues();
copyValues(startValues, internalStartValues);
} else {
internalStartValues = null;
}
if (endValues != null) {
internalEndValues = new android.transition.TransitionValues();
copyValues(endValues, internalEndValues);
} else {
internalEndValues = null;
}
return mTransition.createAnimator(sceneRoot, internalStartValues, internalEndValues);
}
Explode源碼
- Explode結構如下:
public class Explode extends Visibility {
public Explode() {
setPropagation(new CircularPropagation());
}
private void captureValues(TransitionValues transitionValues) {
}
//初始值
@Override
public void captureStartValues(TransitionValues transitionValues) {
super.captureStartValues(transitionValues);
captureValues(transitionValues);
}
//結束值
@Override
public void captureEndValues(TransitionValues transitionValues) {
super.captureEndValues(transitionValues);
captureValues(transitionValues);
}
// 進入時被調用
@Override
public Animator onAppear(ViewGroup sceneRoot, View view,
TransitionValues startValues, TransitionValues endValues) {
//執行動畫
return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
startX, startY, endX, endY, sDecelerate, this);
}
//退出頁面時調用
@Override
public Animator onDisappear(ViewGroup sceneRoot, View view,
TransitionValues startValues, TransitionValues endValues) {
return TranslationAnimationCreator.createAnimation(view, startValues,
viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate, this);
}
private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
}
}
太簡單了,,就是調用 TranslationAnimationCreator.createAnimation
創建動畫....,我們上面的自定義過渡動畫就是跟 這貨學習到....
5. 兼容低版本
到了這里, 送大家一首歌:感覺身體被掏空.......
由于復雜度的原因,
低版本兼容還是說下思路吧,以后有時間或者有明確需求在去實現:
思路:
- 將第一個頁面中共享元素拿出保存。
- 在第二個頁面的onCreate()方法內,設置動畫,并執行第一個頁面view的動畫。
- 第二個界面退出時,執行第二個界面共享元素動畫。
大玩一發——MaterialDesign元素轉換動畫兼容低版本(有可能不會寫!)
網絡上有人實現過 大概是這種思路:https://github.com/huzenan/EasyTransition,具體代碼我沒運行過,不過目測應該是可行的。
參考:
- https://github.com/codepath/android_guides/wiki/Shared-Element-Activity-Transition
- http://www.bkjia.com/Androidjc/900882.html
- http://www.lxweimin.com/p/37e94f8b6f59
- http://www.lxweimin.com/p/cc6cb29ec298
- http://www.open-open.com/lib/view/open1477879867267.html
demo地址:戳我!!!
希望我的文章不會誤導在觀看的你,如果有異議的地方歡迎討論和指正。
如果能給觀看的你帶來收獲,那就是最好不過了。