編寫(xiě): allenlsy - 原文: https://developer.android.com/training/material/animations.html
Material Design中的動(dòng)畫(huà)對(duì)用戶的動(dòng)作進(jìn)行反饋,并提供在整個(gè)交互過(guò)程中的視覺(jué)連續(xù)性。Material 主題為按鈕和Activity切換提供一些默認(rèn)的動(dòng)畫(huà),Android 5.0 (API level 21) 及以上版本支持自定義這些動(dòng)畫(huà)并創(chuàng)建新動(dòng)畫(huà):
Material Design中的觸摸反饋,是在用戶與UI元素交互時(shí),提供視覺(jué)上的即時(shí)確認(rèn)。按鈕的默認(rèn)觸摸反饋動(dòng)畫(huà)使用了新的RippleDrawable類,它在按鈕狀態(tài)變換時(shí)產(chǎn)生波紋效果。
大多數(shù)情況下,你需要在你的 XML 文件中設(shè)定視圖的背景來(lái)實(shí)現(xiàn)這個(gè)功能:
?android:attr/selectableItemBackground 用于有界Ripple動(dòng)畫(huà)?android:attr/selectableItemBackgroundBorderless 用于越出視圖邊界的動(dòng)畫(huà)。它會(huì)被繪制在最近的且不是全屏的父視圖上。Note:
selectableItemBackgroundBorderless是 API level 21 新加入的屬性
另外,你可以使用ripple元素在XML資源文件中定義一個(gè) RippleDrawable。
你可以給RippleDrawable賦予一個(gè)顏色。要改變默認(rèn)的觸摸反饋顏色,使用主題的android:colorControlHighlight 屬性。
更多信息,參見(jiàn)RippleDrawable類的API文檔。
填充效果在UI元素出現(xiàn)或隱藏時(shí),為用戶提供視覺(jué)連續(xù)性。ViewAnimationUtils.createCircularReveal()方法可以使用一個(gè)附著在視圖上的圓形,顯示或隱藏這個(gè)視圖。
要用此效果顯示一個(gè)原本不可見(jiàn)的視圖:
// previously invisible view
View myView = findViewById(R.id.my_view);
// get the center for the clipping circle
int cx = (myView.getLeft() + myView.getRight()) / 2;
int cy = (myView.getTop() + myView.getBottom()) / 2;
// get the final radius for the clipping circle
int finalRadius = myView.getWidth();
// create and start the animator for this view
// (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
anim.start();
要用此效果隱藏一個(gè)原本可見(jiàn)的視圖:
// previously visible view
final View myView = findViewById(R.id.my_view);
// get the center for the clipping circle
int cx = (myView.getLeft() + myView.getRight()) / 2;
int cy = (myView.getTop() + myView.getBottom()) / 2;
// get the initial radius for the clipping circle
int initialRadius = myView.getWidth();
// create the animation (the final radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
// make the view invisible when the animation is done
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
myView.setVisibility(View.INVISIBLE);
}
});
// start the animation
anim.start();
http://wiki.jikexueyuan.com/project/android-training-geek/images/ContactsAnim.gif" alt="Figure 1 - A transition with shared elements." />
Material Design中的Activity切換,當(dāng)不同Activity之間擁有共有元素,則可以通過(guò)不同狀態(tài)之間的動(dòng)畫(huà)和形變提供視覺(jué)上的連續(xù)性。你可以為共有元素設(shè)定進(jìn)入和退出Activity時(shí)的自定義動(dòng)畫(huà)。
Android 5.0 (API level 21) 支持這些入場(chǎng)和退出變換:
任何繼承于 Visibility 類的變換,都支持被用于入場(chǎng)或退出變換。更多信息,請(qǐng)參見(jiàn) Transition 類的API文檔。
Android 5.0 (API level 21) 還支持這些共有元素變換效果:
當(dāng)你在應(yīng)用中進(jìn)行activity 變換時(shí),默認(rèn)的淡入淡出效果會(huì)被用在進(jìn)入和退出activity的過(guò)程中。
http://wiki.jikexueyuan.com/project/android-training-geek/images/SceneTransition.png" alt="" />
首先,當(dāng)你繼承Material主題的style時(shí),要通過(guò)android:windowContentTransitions屬性來(lái)開(kāi)啟窗口內(nèi)容變換功能。你也可以在style定義中聲明進(jìn)入、退出和共有元素切換:
<style name="BaseAppTheme" parent="android:Theme.Material">
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify enter and exit transitions -->
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>
<!-- specify shared element transitions -->
<item name="android:windowSharedElementEnterTransition">
@transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">
@transition/change_image_transform</item>
</style>
例子中的change_image_transform 切換定義如下:
<!-- res/transition/change_image_transform.xml -->
<!-- (see also Shared Transitions below) -->
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeImageTransform/>
</transitionSet>
changeImageTransform 元素對(duì)應(yīng) ChangeImageTransform 類。更多信息,請(qǐng)參見(jiàn) Transition類的API文檔。
要在代碼中啟用窗口內(nèi)容切換,調(diào)用Window.requestFeature()函數(shù):
// inside your activity (if you did not enable transitions in your theme)
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
// set an exit transition
getWindow().setExitTransition(new Explode());
要聲明變換類型,就要在Transition對(duì)象上調(diào)用以下函數(shù):
Window.setEnterTransition()Window.setExitTransition()Window.setSharedElementEnterTransition()Window.setSharedElementExitTransition()setExitTransition() 和 setSharedElementExitTransition() 函數(shù)為activity定義了退出變換效果。setEnterTransition() 和 setSharedElementEnterTransition() 函數(shù)定義了進(jìn)入activity的變換效果。
要獲得切換的全部效果,你必須在出入的兩個(gè)activity中都開(kāi)啟窗口內(nèi)容切換。否則,調(diào)用的activity會(huì)使用退出效果,但是接著你會(huì)看到一個(gè)傳統(tǒng)的窗口切換(比如縮放或淡入淡出)。
要盡早開(kāi)始入場(chǎng)切換,可以在被調(diào)用的Activity上使用Window.setAllowEnterTransitionOverlap() 。它可以使你擁有更戲劇性的入場(chǎng)切換。
如果你開(kāi)啟Activity入場(chǎng)和退出效果,那么當(dāng)你在用如下方法開(kāi)始Activity時(shí),切換效果會(huì)被應(yīng)用:
startActivity(intent,
ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
如果你為第二個(gè)Activity設(shè)定了入場(chǎng)變換,變換也會(huì)在activity開(kāi)始時(shí)被啟用。要在開(kāi)始另一個(gè)acitivity時(shí)禁用變換,可以給bundle的選項(xiàng)提供一個(gè)null對(duì)象:
要在兩個(gè)擁有共用元素的activity間進(jìn)行切換動(dòng)畫(huà):
android:transitionName屬性在兩個(gè)layout文件中給共有元素賦予同一個(gè)名字ActivityOptions.makeSceneTransitionAnimation()方法// get the element that receives the click event
final View imgContainerView = findViewById(R.id.img_container);
// get the common element for the transition in this activity
final View androidRobotView = findViewById(R.id.image_small);
// define a click listener
imgContainerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(this, Activity2.class);
// create the transition animation - the images in the layouts
// of both activities are defined with android:transitionName="robot"
ActivityOptions options = ActivityOptions
.makeSceneTransitionAnimation(this, androidRobotView, "robot");
// start the new activity
startActivity(intent, options.toBundle());
}
});
對(duì)于用代碼編寫(xiě)的共有動(dòng)態(tài)視圖,使用View.setTransitionName()方法來(lái)在兩個(gè)activity中定義共有元素。
要在第二個(gè)activity結(jié)束時(shí)進(jìn)行逆向的場(chǎng)景切換動(dòng)畫(huà),調(diào)用Activity.finishAfterTransition()方法,而不是Activity.finish()。
要在擁有多個(gè)共有元素的activity之間使用變換動(dòng)畫(huà),就要用android:transitionName屬性在兩個(gè)layout中定義這個(gè)共有元素(或在兩個(gè)Activity中使用View.setTransitionName()方法),再創(chuàng)建ActivityOptions對(duì)象:
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
Pair.create(view1, "agreedName1"),
Pair.create(view2, "agreedName2"));
Material Design中的動(dòng)畫(huà)可以表示為基于時(shí)間插值和空間移動(dòng)模式的曲線。在Android 5.0 (API level 21)以上版本中,你可以為動(dòng)畫(huà)定義時(shí)間曲線和曲線動(dòng)畫(huà)模式。
PathInterpolator類是一個(gè)基于貝澤爾曲線或Path對(duì)象的新的插值方法。插值方法 是一個(gè)定義在 1x1 正方形中的曲線函數(shù)圖像,其始末兩點(diǎn)分別在(0,0)和(1,1),一個(gè)用構(gòu)造函數(shù)定義的控制點(diǎn)。你也可以使用XML資源文件定義一個(gè)插值方法:
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:controlX1="0.4"
android:controlY1="0"
android:controlX2="1"
android:controlY2="1"/>
Material Design標(biāo)準(zhǔn)中,系統(tǒng)提供了三種基本的曲線:
@interpolator/fast_out_linear_in.xml@interpolator/fast_out_slow_in.xml@interpolator/linear_out_slow_in.xml你可以將一個(gè)PathInterpolator對(duì)象傳給Animator.setInterpolator()方法。
ObjectAnimator類有一個(gè)新的構(gòu)造函數(shù),使你可以沿一條路徑使用多個(gè)屬性來(lái)在坐標(biāo)系中進(jìn)行變換。比如,以下animator(動(dòng)畫(huà)器,譯者注)使用一個(gè)Path對(duì)象來(lái)改變一個(gè)試圖的X和Y屬性:
ObjectAnimator mAnimator;
mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
...
mAnimator.start();
StateListAnimator 類是你可以定義在視圖狀態(tài)改變啟動(dòng)的Animator(動(dòng)畫(huà)器,譯者注)。以下例子展示如何在XML文件中定義StateListAnimator:
<!-- animate the translationZ property of a view when pressed -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="2dp"
android:valueType="floatType"/>
<!-- you could have other objectAnimator elements
here for "x" and "y", or other properties -->
</set>
</item>
<item android:state_enabled="true"
android:state_pressed="false"
android:state_focused="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="100"
android:valueTo="0"
android:valueType="floatType"/>
</set>
</item>
</selector>
要把視圖改變Animator關(guān)聯(lián)到一個(gè)視圖,就要在XML資源文件的selector元素上定義一個(gè)Animator,并把此Animator賦值給視圖的 android:stateListAnimator 屬性。要想在Java代碼中將狀態(tài)列表Animator賦值給視圖,使用AnimationInflater.loadStateListAnimator() 函數(shù),并用View.setStateListAnimator()函數(shù)把Animator賦值給你的視圖。
當(dāng)你的主題繼承于Material Theme的時(shí)候,Button默認(rèn)會(huì)有一個(gè)Z值動(dòng)畫(huà)。為了避免Button的Z值動(dòng)畫(huà),設(shè)定它的android:stateListAnimator屬性為@null。
AnimatedStateListDrawable類使你可以創(chuàng)建一個(gè)在視圖狀態(tài)變化之間顯示動(dòng)畫(huà)的drawable。有一些Android 5.0系統(tǒng)組件默認(rèn)已經(jīng)使用了這些動(dòng)畫(huà)。下面的例展示如何在XML資源文件中定義AnimatedStateListDrawable:
<!-- res/drawable/myanimstatedrawable.xml -->
<animated-selector
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- provide a different drawable for each state-->
<item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
android:state_pressed="true"/>
<item android:id="@+id/focused" android:drawable="@drawable/drawableF"
android:state_focused="true"/>
<item android:id="@id/default"
android:drawable="@drawable/drawableD"/>
<!-- specify a transition -->
<transition android:fromId="@+id/default" android:toId="@+id/pressed">
<animation-list>
<item android:duration="15" android:drawable="@drawable/dt1"/>
<item android:duration="15" android:drawable="@drawable/dt2"/>
...
</animation-list>
</transition>
...
</animated-selector>
矢量Drawable是可以無(wú)損縮放的。AnimatedVectorDrawable類是你可以操作矢量Drawable。
你通常在3個(gè)XML文件中定義動(dòng)畫(huà)矢量Drawable:
res/drawable/中用<vector>定義一個(gè)矢量drawableres/drawable/中用<animated-vector>定義一個(gè)動(dòng)畫(huà)矢量drawable動(dòng)畫(huà)矢量drawable可以用在<group>和<path>元素的屬性上。<group>元素定義了一些path或者subgroup,<path>定義了一條被繪畫(huà)的路徑。
當(dāng)你想要定義一個(gè)動(dòng)畫(huà)的矢量drawable時(shí),使用android:name 屬性來(lái)為group和path賦值一個(gè)唯一的名字(name),這樣你可以通過(guò)animator的定義找到他們。比如:
<!-- res/drawable/vectordrawable.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp"
android:width="64dp"
android:viewportHeight="600"
android:viewportWidth="600">
<group
android:name="rotationGroup"
android:pivotX="300.0"
android:pivotY="300.0"
android:rotation="45.0" >
<path
android:name="v"
android:fillColor="#000000"
android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
</group>
</vector>
動(dòng)畫(huà)矢量drawable的定義是通過(guò)name屬性來(lái)找到視圖組(group)和路徑(path)的:
<!-- res/drawable/animvectordrawable.xml -->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vectordrawable" >
<target
android:name="rotationGroup"
android:animation="@anim/rotation" />
<target
android:name="v"
android:animation="@anim/path_morph" />
</animated-vector>
動(dòng)畫(huà)的定義代表ObjectAnimator或者AnimatorSet對(duì)象。例子中第一個(gè)animator將目標(biāo)組旋轉(zhuǎn)了360度。
<!-- res/anim/rotation.xml -->
<objectAnimator
android:duration="6000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
第二個(gè)animator將矢量drawable的路徑從一個(gè)形狀(morph)變形到另一個(gè)。兩個(gè)路徑都必須是可以形變的:他們必須有相同數(shù)量的命令,每個(gè)命令必須有相同數(shù)量的參數(shù)
<!-- res/anim/path_morph.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="3000"
android:propertyName="pathData"
android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"
android:valueType="pathType" />
</set>
更多信息,請(qǐng)參考AnimatedVectorDrawable的API指南。