Material-Animaltion
這是GitHub上的一個(gè)開(kāi)源項(xiàng)目(點(diǎn)擊傳送門(mén)), 演示View的平移蛮粮、縮放動(dòng)畫(huà)松却,activity進(jìn)入和退出動(dòng)畫(huà)翅楼,界面間元素共享理澎。
Andorid Transitions Framework
作用
可以在activity之間跳轉(zhuǎn)的時(shí)候添加動(dòng)畫(huà)
動(dòng)畫(huà)共享元素之間的轉(zhuǎn)換活動(dòng)
activity中布局元素的過(guò)渡動(dòng)畫(huà)酥宴。
1. Transitions between Activitys
- Animate existing activity layout content
![image](https://raw.githubusercontent.com/lgvalle/Material-Animations/master/screenshots/transition_A_to_B.png)
當(dāng)過(guò)渡從activity到activity內(nèi)容布局是根據(jù)定義的過(guò)渡動(dòng)畫(huà)弯淘。有三個(gè)預(yù)定義的轉(zhuǎn)換android.transition上可用榆综。轉(zhuǎn)換可以使用:Explode,Slide和Fade妙痹。所有這些轉(zhuǎn)換跟蹤更改目標(biāo)的可見(jiàn)性活動(dòng)視圖布局和動(dòng)畫(huà)那些觀點(diǎn)遵循轉(zhuǎn)換規(guī)則。
Explode | Slide | Fade |
---|---|---|
image
|
image
|
![]() image
|
實(shí)現(xiàn)這些效果可以通過(guò)xml方式或者在直接在類中實(shí)現(xiàn),下面是Fade的實(shí)現(xiàn)方式鼻疮。
說(shuō)明:Fade
- 如果是xml方式實(shí)現(xiàn)怯伊,首先在/res下創(chuàng)建transition文件夾。
res/transition/slide_from_right
<?xml version = 1.0 encoding = "utf-8"?>
<transitionSet xmls:android = "http://schemas.android.com/apk/res/android">
<slide duration = "500"
slideEage = "left"/>
</transitionSet>
2.如果直接用代碼實(shí)現(xiàn)陋守,可以如下實(shí)現(xiàn)
MainActivity
Slide slideTracition = newSlide();
slideTracition.setSlideEdge(Gravity.LEFT);
slideTracition.setDuration(getResources().getInteger(R.integer.anim_duration_long));
- 實(shí)現(xiàn)
MianActivity.onCreat();
設(shè)置MainActivty的進(jìn)出動(dòng)畫(huà)代碼如下
private void setupWindowAnimations() {
Transition slideTracition = TransitionInflater.from(this).inflateTransition(R.transition.slide_from_left);
getWindow().setEnterTransition(slideTracition);
getWindow().setExitTransition(slideTracition);
//getWindow().setReenterTransition(buildExitTransition());
}
- 分析Fade的步驟是怎么發(fā)生的
- ActivatyA 啟動(dòng) ActivityB
- Transition Framework找到一個(gè)ExitTransition震贵,并將其應(yīng)用于所有可見(jiàn)視圖。
- 在返回之前過(guò)渡框架執(zhí)行進(jìn)入和退出分別反向動(dòng)畫(huà)(如果我們定義的輸出returnTransition和reenterTransition水评,這些都已經(jīng)轉(zhuǎn)而執(zhí)行)
ReturnTransition&ReenterTransition
返回和重新輸入轉(zhuǎn)換分別是Enter和Exit的反向動(dòng)畫(huà)猩系。
- EnterTransition < - > ReturnTransition
- ExitTransition < - > ReenterTransition
如果未定義返回或重新輸入,Android將按照你之前設(shè)定的默認(rèn)的版本中燥。但是如果你定義它們寇甸,你可以有不同的轉(zhuǎn)換進(jìn)入和退出活動(dòng)。(如下圖)
- 當(dāng)你從ActivityB返回到ActivityA的時(shí)候疗涉。需要重新制定ActivityB的退出動(dòng)畫(huà)拿霉,可以通過(guò)如下方式
Visiable slide = new Slide();
slide.setDuraing(500);
getWindow.setReturnTransition(slide);
//別直接調(diào)用finish();
finshAfterTransition();
效果圖
Without Return Transition | With Return Transition |
---|---|
![]() image
|
![]() image
|
2.Share elements between Activity(元素共享)
共享元素過(guò)度動(dòng)畫(huà)的背后是通過(guò)過(guò)度動(dòng)畫(huà)將兩個(gè)不同布局中的不同view關(guān)聯(lián)起來(lái)。Transition框架知道用適當(dāng)?shù)膭?dòng)畫(huà)向用戶展示從一個(gè)view向另外 一個(gè)view過(guò)度咱扣。請(qǐng)記渍捞浴:共享元素過(guò)度的過(guò)程中,view并沒(méi)有真正從一個(gè)布局跑到另外一個(gè)布局闹伪,整個(gè)過(guò)程基本都是在后一個(gè)布局中完成的沪铭。
![image](https://raw.githubusercontent.com/lgvalle/Material-Animations/master/screenshots/shared_element.png)
a) 允許過(guò)度動(dòng)畫(huà)
需要在/res/style.xml添加
<item name="android:windowContentTransitions">true</item>
b) 在對(duì)應(yīng)的xml文件指定TransitionName屬性
res/layout/activity
- 指定ImageView和TextView
注意:這里必須為共享的倆個(gè)元素指定同一個(gè)TransitionName壮池,不然不會(huì)出現(xiàn)共享效果
<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) 啟動(dòng)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());
這段代碼將生成這個(gè)美麗的過(guò)渡動(dòng)畫(huà):
那么在Fragment之間怎么實(shí)現(xiàn)呢?
a) 允許過(guò)度動(dòng)畫(huà)
需要在/res/style.xml添加
<item name="android:windowContentTransitions">true</item>
b) 在對(duì)應(yīng)的xml文件指定TransitionName屬性
之前的倆個(gè)步驟和Activity之間共享元素的沒(méi)有太大差別杀怠。
c) 通過(guò)Shared Element方式啟動(dòng)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.動(dòng)畫(huà)視圖布局元素
上面?zhèn)z種方式都是運(yùn)用于過(guò)度動(dòng)畫(huà)椰憋,那Transition FrameWork也可以被用做于改變布局中的某個(gè)特定的View,比如修改View的位置或者大小赔退。我們需要確定想改變的結(jié)果即可橙依。
1.我們需要告訴framework我們需要改動(dòng)界面的Ui
TransitionManager.beginDelayedTransition(viewRoot);
- 這里的viewRoot是需要改變view當(dāng)前所在的根布局
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 |
---|---|
image
|
image
|
4.共享元素+循環(huán)動(dòng)畫(huà)
循環(huán)顯示只是一個(gè)動(dòng)畫(huà)顯示或隱藏一組UI元素。它可以自API21或以上使用硕旗。
![image](http://olcfylmob.bkt.clouddn.com/shared_reveal_anim.gif-sizeImage)
上圖發(fā)生了幾個(gè)步驟彭沼?了解了之前的共享元素后我們知道智什。
- 黃色的小球共享于MainActivity和RevealActivity樱衷。
- 當(dāng)共享動(dòng)畫(huà)結(jié)束之后榆骚,在RevealActivity中發(fā)生了倆組動(dòng)畫(huà)效果
- Toolbar上
- 底部四個(gè)球執(zhí)行了特定的動(dòng)畫(huà)
那我們應(yīng)該如何監(jiān)聽(tīng)共享元素的動(dòng)畫(huà)結(jié)束的時(shí)刻。
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();
}
});
}
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();
}
四個(gè)小球的浮現(xiàn)的動(dòng)畫(huà)
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);
}
}
另一種效果
紅色小球的效果
紅球運(yùn)動(dòng)軌跡 res/transition/changebounds_with_arcmotion.xml(這里不是共享動(dòng)畫(huà))
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>
點(diǎn)擊紅色小球時(shí) 執(zhí)行 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;
}
更多信息
- 如果你想深入了解和學(xué)習(xí)更多的有關(guān)于過(guò)度動(dòng)畫(huà)(Transition Framework)的內(nèi)容浪读。可以訪問(wèn) Alex Lockwood 的帖子 http : //www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html
- 驚人的存儲(chǔ)庫(kù)與許多材料設(shè)計(jì)樣本由Saul Molinero: https://github.com/saulmm/Android-Material-Examples
- Chet Hasse視頻詳細(xì)講解了過(guò)渡框架: https://www.youtube.com/watch?v=S3H7nJ4QaD8