Android開源:Material-Animations - 過場動畫
Material-Animaltion
這是GitHub上的一個開源項目, 演示View的平移、縮放動畫,activity進入和退出動畫,界面間元素共享。
Andorid Transitions Framework
作用
-
可以在activity之間跳轉的時候添加動畫
-
動畫共享元素之間的轉換活動
-
activity中布局元素的過渡動畫。
1. Transitions between Activitys
- Animate existing activity layout content
當過渡從activity到activity內容布局是根據定義的過渡動畫。有三個預定義的轉換android.transition上可用。轉換可以使用:Explode,Slide和Fade。所有這些轉換跟蹤更改目標的可見性活動視圖布局和動畫那些觀點遵循轉換規則。
Explode | Slide | Fade |
---|---|---|
![]() |
![]() |
![]() |
現這些效果可以通過xml方式或者在直接在類中實現,下面是Fade的實現方式。
### 說明:Fade
- 如果是xml方式實現,首先在/res下創建transition文件夾。
res/transition/slide_from_right
<?xml version = 1.0 encoding = "utf-8"?>
<transitionSet xmls:android = ";
<slide duration = "500"
slideEage = "left"/>
</transitionSet></code></pre>
2.如果直接用代碼實現,可以如下實現
MainActivity
Slide slideTracition = newSlide();
slideTracition.setSlideEdge(Gravity.LEFT);
slideTracition.setDuration(getResources().getInteger(R.integer.anim_duration_long));</code></pre>
- 實現
MianActivity.onCreat();
設置MainActivty的進出動畫代碼如下
private void setupWindowAnimations() {
Transition slideTracition = TransitionInflater.from(this).inflateTransition(R.transition.slide_from_left);
getWindow().setEnterTransition(slideTracition);
getWindow().setExitTransition(slideTracition);
//getWindow().setReenterTransition(buildExitTransition());
}
- 分析Fade的步驟是怎么發生的
- ActivatyA 啟動 ActivityB
- Transition Framework找到一個ExitTransition,并將其應用于所有可見視圖。
- 在返回之前過渡框架執行進入和退出分別反向動畫(如果我們定義的輸出returnTransition和reenterTransition,這些都已經轉而執行)
</li>
</ol>
ReturnTransition&ReenterTransition
返回和重新輸入轉換分別是Enter和Exit的反向動畫。
- EnterTransition < - > ReturnTransition
- ExitTransition < - > ReenterTransition
如果未定義返回或重新輸入,Android將按照你之前設定的默認的版本。但是如果你定義它們,你可以有不同的轉換進入和退出活動。(如下圖)

- 當你從ActivityB返回到ActivityA的時候。需要重新制定ActivityB的退出動畫,可以通過如下方式
Visiable slide = new Slide();
slide.setDuraing(500);
getWindow.setReturnTransition(slide);
//別直接調用finish();
finshAfterTransition();
效果圖
Without Return Transition
With Return Transition


2.Share elements between Activity(元素共享)
共享元素過度動畫的背后是通過過度動畫將兩個不同布局中的不同view關聯起來。Transition框架知道用適當的動畫向用戶展示從一個view向另外 一個view過度。請記住:共享元素過度的過程中,view并沒有真正從一個布局跑到另外一個布局,整個過程基本都是在后一個布局中完成的。

a) 允許過度動畫
需要在/res/style.xml添加
<item name="android:windowContentTransitions">true</item>
b) 在對應的xml文件指定TransitionName屬性
res/layout/activity
- 指定ImageView和TextView
注意:這里必須為共享的倆個元素指定同一個TransitionName,不然不會出現共享效果
<ImageView
android:id="@+id/square_blue"
style="@style/MaterialAnimations.Icon.Big"
android:src="@drawable/circle_24dp"
android:transitionName="@string/square_blue_name" />
<TextView
android:id="@+id/title"
style="@style/MaterialAnimations.TextAppearance.Title.Invers"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:text="@{sharedSample.name}"
android:transitionName="@string/sample_blue_title" />
c) 啟動Activity
Intent intent = new Intent(activity,target);
ActivityOptionCompat option = ActiviyoptionCampat.makeSceneTransitionAnimation(activity,
new Pair<View, String>(viewHolder.binding.sampleIcon, activity.getString(R.string.square_blue_name)),
new Pair<View, String>(viewHolder.binding.sampleName, activity.getString(R.string.sample_blue_title)));
startActivity(intent,option.toBundle());</code></pre>
這段代碼將生成這個美麗的過渡動畫:

那么在Fragment之間怎么實現呢?
a) 允許過度動畫
需要在/res/style.xml添加
<item name="android:windowContentTransitions">true</item>
b) 在對應的xml文件指定TransitionName屬性
之前的倆個步驟和Activity之間共享元素的沒有太大差別。
c) 通過Shared Element方式啟動fragment
private void addNextFragment(Sample sample, ImageView blue, boolean b) {
SharedElementFragment2 elementFragment2 = SharedElementFragment2.newInstance(sample);
Slide slide = new Slide();
slide.setDuration(getResources().getInteger(R.integer.anim_duration_medium));
slide.setSlideEdge(Gravity.RIGHT);
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(getResources().getInteger(R.integer.anim_duration_medium));
elementFragment2.setEnterTransition(slide);
elementFragment2.setAllowEnterTransitionOverlap(b);
elementFragment2.setAllowReturnTransitionOverlap(b);
elementFragment2.setSharedElementEnterTransition(changeBounds);
getFragmentManager().beginTransaction().replace(R.id.sample2_content, elementFragment2).addToBackStack(null).addSharedElement(blue, getString(R.string.square_blue_name)).commit();
}
效果如下

3.動畫視圖布局元素
上面倆種方式都是運用于過度動畫,那Transition FrameWork也可以被用做于改變布局中的某個特定的View,比如修改View的位置或者大小。我們需要確定想改變的結果即可。
1.我們需要告訴framework我們需要改動界面的Ui
TransitionManager.beginDelayedTransition(viewRoot);
- 這里的viewRoot是需要改變view當前所在的根布局
2.改變View的屬性
- 改變大小
ViewGroup.LayoutParams params = view.getLayoutParams();
params.witdh = 200;
view.setlayoutParams(params);
- 改變位置
ViewGroup.LayoutParams params = view.getLayoutParams();
params.gravity = Gravity.Left;
view.setlayoutParams(params);
Size
Position


4.共享元素+循環動畫
循環顯示只是一個動畫顯示或隱藏一組UI元素。它可以自API21或以上使用。

上圖發生了幾個步驟?了解了之前的共享元素后我們知道。
- 黃色的小球共享于MainActivity和RevealActivity。
- 當共享動畫結束之后,在RevealActivity中發生了倆組動畫效果
- Toolbar上
- 底部四個球執行了特定的動畫
</li>
</ul>
那我們應該如何監聽共享元素的動畫結束的時刻。
private void setupEnterAnimations() {
Transition transtion = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
getWindow().setSharedElementEnterTransition(transtion);
transtion.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
hideTarget();
animateRevealShow(toolbar);
animateButtonIn();
}
});
}</code></pre>
res/transition/changebounds_with_arcmotion.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/anim_duration_long"
android:interpolator="@android:interpolator/decelerate_cubic"
>
<changeBounds>
<arcMotion
android:maximumAngle="90"
android:minimumHorizontalAngle="90"
android:minimumVerticalAngle="0"/>
</changeBounds>
</transitionSet>
ToolBar Animation
private void animateRevealShow(View viewRoot) {
int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
int finalRadius = Math.max(viewRoot.getWidth(), viewRoot.getHeight());
Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
viewRoot.setVisibility(View.VISIBLE);
anim.setDuration(1000);
anim.setInterpolator(new AccelerateInterpolator());
anim.start();
}
四個小球的浮現的動畫
private void animateButtonIn() {
for (int i = 0; i < bgViewGroup.getChildCount(); i++) {
View child = bgViewGroup.getChildAt(i);
child.animate()
.setStartDelay(100 + i * DELAY)
.setInterpolator(new AccelerateInterpolator())
.alpha(1)
.scaleX(1)
.scaleY(1);
}
}
另一種效果
紅色小球的效果

紅球運動軌跡 res/transition/changebounds_with_arcmotion.xml(這里不是共享動畫)
res/transition/changebounds_with_arcmotion.xml
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/anim_duration_long"
android:interpolator="@android:interpolator/decelerate_cubic"
>
<changeBounds>
<arcMotion
android:maximumAngle="90"
android:minimumHorizontalAngle="90"
android:minimumVerticalAngle="0"/>
</changeBounds>
</transitionSet>
點擊紅色小球時 執行 revealRed()
-
private void revealRed() {
final ViewGroup.LayoutParams params = btnRed.getLayoutParams();
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.changebounds_with_arcmotion);
transition.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
animateRevealColor(bgViewGroup, R.color.sample_red);
body.setText(R.string.reveal_body3);
body.setTextColor(ContextCompat.getColor(RevealActivity.this, R.color.theme_red_background));
btnRed.setLayoutParams(params);
}
@Override
public void onTransitionCancel(Transition transition) {
}
@Override
public void onTransitionPause(Transition transition) {
}
@Override
public void onTransitionResume(Transition transition) {
}
});
TransitionManager.beginDelayedTransition(bgViewGroup, transition);
final RelativeLayout.LayoutParams relativeRarams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
relativeRarams.addRule(RelativeLayout.CENTER_IN_PARENT);
btnRed.setLayoutParams(relativeRarams);
}
private void animateRevealColor(ViewGroup viewRoot, @ColorRes int color) {
int cx = (viewRoot.getLeft() + viewRoot.getRight()) / 2;
int cy = (viewRoot.getTop() + viewRoot.getBottom()) / 2;
animateRevealColorFromCoordinates(viewRoot, color, cx, cy);
}
private Animator animateRevealColorFromCoordinates(ViewGroup root, @ColorRes int color, int cx, int cy) {
int finalRadius = Math.max(root.getWidth(), root.getHeight());
final Animator animator = ViewAnimationUtils.createCircularReveal(root, cx, cy, 0, finalRadius);
root.setBackgroundColor(ContextCompat.getColor(this, color));
animator.setDuration(getResources().getInteger(R.integer.anim_duration_long));
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
return animator;
}
更多信息
- 如果你想深入了解和學習更多的有關于過度動畫(Transition Framework)的內容。可以訪問 Alex Lockwood 的帖子 http : //www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html
- 驚人的存儲庫與許多材料設計樣本由Saul Molinero: https://github.com/saulmm/Android-Material-Examples
- Chet Hasse視頻詳細講解了過渡框架: https://www.油Tube.com/watch?v=S3H7nJ4QaD8