Material Design中全新的動畫
在Material Design設計中,爲用戶與app交互反饋他們的動作行爲和提供了視覺上的連貫性。Material主題爲控件和Activity的過渡提供了一些默認的動畫,在android L上,允許自定義這些動畫:
- Touch feedback 觸摸反饋
- Circular Reveal 圓形展示
- Curved motion 曲線運動
- View state changes 視圖狀態變化
- Vector Drawables 矢量圖動畫
- Activity transitions 活動轉場
觸摸反饋
觸摸反饋是指用戶在觸摸控件時的一種可視化交互,在Android L之前,通常是通過press色變來凸顯,但是因爲是瞬間變化的效果,不如動畫生動。
在Android L使用了RippleDrawable類,用一個水波紋擴散效果在兩種不同的狀態間過渡。
使用Material Design樣式的應用,button默認帶有該效果。除了默認的效果外,系統還提供了另外兩種效果,我們只把button的背景指定爲:
?android:attr/selectableItemBackground
?android:attr/selectableItemBackgroundBorderless
任何view處於可點擊狀態,都可以使用RippleDrawable來達到水波紋特效。
我們也可以通過設置RippleDrawable的顏色屬性來調節動畫顏色,系統默認的顏色爲主題的一個屬性顏色:android:colorControlHighlight,所以我們可以通過修改該顏色值來統一修改默認的水波紋顏色。android:colorAccent可以修改checkbox的選中顏色,更多顏色設置請參考主題。
系統的三種觸摸反饋都是通過xml構建的,內容如下:
默認:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item>
<inset
android:insetLeft="4dp"
android:insetTop="6dp"
android:insetRight="4dp"
android:insetBottom="6dp">
<shape android:shape="rectangle">
<corners android:radius="2dp" />
<solid android:color="?android:attr/colorButtonNormal" />
<padding android:left="8dp"
android:top="4dp"
android:right="8dp"
android:bottom="4dp" />
</shape>
</inset>
</item>
</ripple>
?android:attr/selectableItemBackground
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
<color android:color="@android:color/white" />
</item>
</ripple>
?android:attr/selectableItemBackgroundBorderless
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight"
/>
代碼中設置
RippleDrawableColorStateList stateList =
getResources().getColorStateList(R.color.tint_state_color);
RippleDrawable rippleDrawable = new RippleDrawable(stateList,null,null);
view.setBackground(rippleDrawable);
圓形展示
我們通常會顯示或者隱藏一個view,在Android L之前,這是一個生硬瞬間變化動作,現在,有了一個新的api爲此效果提供一個圓形的顯示或者隱藏的動畫效果。
RevealAnimator和之前的動畫使用沒什麼區別,同樣可以設置監聽器和加速器來實現各種各樣的特效,該動畫主要用在隱藏或者顯示一個view,改變view的大小等過渡效果。
通過ViewAnimationUtils.createCircularReveal
來創建一個動畫,該api接受5個參數:
- view 操作的視圖
- centerX 動畫開始的中心點X
- centerY 動畫開始的中心點Y
- startRadius 動畫開始半徑
- startRadius 動畫結束半徑
沿着中心的縮小的動畫
Animator animator = ViewAnimationUtils.createCircularReveal(view, view.getWidth() / 2,
view.getHeight() / 2, view.getWidth(), 0);
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(1000);
animator.start();
從左上角擴展的圓形動畫
Animator animator = ViewAnimationUtils.createCircularReveal(view,0,0,0,(float)
Math.hypot(view.getWidth(), view.getHeight()));
animator.setDuration(1000);
animator.start();
曲線運動
曲線動畫在Android L之前我們可以通過繼承位移動畫重載applyTransformation函數來實現運動軌跡算法,但是操作起來比較繁瑣:
通過繼承位移動畫,來改寫applyTransformation來修改位移的軌跡
通過繼承位移動畫,來改寫applyTransformation來修改位移的軌跡
public class ArcTranslateAnimation extends Animation {
private float mFromXValue,mToXValue,mFromYValue,mToYValue;
private float mFromXDelta,mToXDelta,mFromYDelta,mToYDelta;
private PointF mStart,mControl,mEnd;
public ArcTranslateAnimation(float fromXValue, float toXValue, float fromYValue, float toYValue) {
mFromXValue = fromXValue;
mToXValue = toXValue;
mFromYValue = fromYValue;
mToYValue = toYValue;
}
protected void applyTransformation(float interpolatedTime, Transformation t) {
float dx = calcBezier(interpolatedTime, mStart.x, mControl.x, mEnd.x);
float dy = calcBezier(interpolatedTime, mStart.y, mControl.y, mEnd.y);
t.getMatrix().setTranslate(dx, dy);
}
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mFromXDelta = resolveSize(ABSOLUTE, mFromXValue, width, parentWidth);
mToXDelta = resolveSize(ABSOLUTE, mToXValue, width, parentWidth);
mFromYDelta = resolveSize(ABSOLUTE, mFromYValue, height, parentHeight);
mToYDelta = resolveSize(ABSOLUTE, mToYValue, height, parentHeight);
mStart = new PointF(mFromXDelta, mFromYDelta);
mEnd = new PointF(mToXDelta, mToYDelta);
mControl = new PointF(mFromXDelta, mToYDelta);
}
private long calcBezier(float interpolatedTime, float p0, float p1, float p2) {
return Math.round((Math.pow((1 - interpolatedTime), 2) * p0)
+ (2 * (1 - interpolatedTime) * interpolatedTime * p1) +
(Math.pow(interpolatedTime, 2) * p2);
}
}
現在我們有了更簡單的實現方式。
ObjectAnimator新增了path方式來構建動畫,並且可以同時對x,y兩個屬性做動畫,我們只用指定一個曲線的path,即可作出曲線的動畫,可以用quadTo/cubicTo繪製貝塞爾曲線,也可以使用arcTo繪製普通的弧線 新增了PathInterpolator動畫插入器,新的基於貝塞爾曲線或路徑對象的插入器。這個插入器指定了一個1x1
正方形運動曲線,它使用(0,0)爲錨點,(1,1)爲控制點,作爲構造函數的參數。
Path path = new Path();
path.moveTo(100,100);
path.quadTo(1000,300,300,700);
ObjectAnimator mAnimator = ObjectAnimator.ofFloat(curved, View.X, View.Y, path);
Path p = new Path();
p.lineTo(0.6f, 0.9f);
p.lineTo(0.75f, 0.2f);
p.lineTo(1f, 1f);
mAnimator.setInterpolator(new PathInterpolator(p));
mAnimator.setDuration(3000);
mAnimator.start();
視圖狀態變化
Android L在原有的圖片選擇器和顏色選擇器上進行了增強,不僅是控件能根據不同的狀態顯示不同的背景圖片,還能在兩種狀態切換時指定一個動畫,來增加過渡效果,吸引用戶眼球,以突出重點內容。
StateListAnimator類和圖片選擇器,顏色選擇器類似,可以根據view的狀態改變呈現不同的動畫效果,通過xml我們可以構建對應不同狀態的動畫合集,其使用方式也非常簡單,在對應的狀態指定一個屬性動畫即可:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="200"
android:valueTo="20dp"
android:valueType="floatType"/>
</set>
</item>
<item android:state_enabled="true" android:state_pressed="false">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="200"
android:valueTo="0"
android:valueType="floatType"/>
</set>
</item>
</selector>
在xml中通過android:stateListAnimator
來指定狀態動畫,代碼中可以通過AnimationInflater.loadStateListAnimator()加載動畫,並使用View.setStateListAnimator()將其指定給View。
可以在狀態切換的過程指定多個屬性動畫的合集,繼承了Material主題後,按鈕默認擁有了z屬性動畫。如果想取消這種默認狀態,可以把狀態動畫指定爲null。
除了StateListAnimator類指定狀態切換的屬性動畫外,還可以通過AnimatedStateListDrawable來指定狀態切換的幀動畫:
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/pressed" android:drawable="@drawable/btn_check_15"
android:state_pressed="true"/>
<item android:id="@+id/normal" android:drawable="@drawable/btn_check_0"/>
<transition android:fromId="@+id/normal" android:toId="@+id/pressed">
<animation-list>
<item android:duration="20" android:drawable="@drawable/btn_check_0"/>
<item android:duration="20" android:drawable="@drawable/btn_check_1"/>
<item android:duration="20" android:drawable="@drawable/btn_check_2"/>
</animation-list>
</transition>
</animated-selector>
矢量圖動畫
前面我們學習了矢量圖,AnimatedVectorDrawable類讓你能使一個矢量圖動起來。矢量圖動畫比幀動畫更平滑的展現圖片的變化過程,並且無論在內存佔用,還是包體積佔用上都要優於幀動畫。通常定義一個矢量圖動畫需要三步:
在drawable資源目錄下定義一個矢量圖
<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>
在anim下頂一個objectAnimator,並在動畫中修改矢量圖的path
<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,0z"
android:valueType="pathType" />
</set>
在drawable下定義一個animated-vector,並把drawable指向矢量圖,把target中的動畫指定爲之前定義的objectAnimator
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable" >
<target
android:name="v"
android:animation="@anim/vector_anim" />
</animated-vector>
轉場動畫
在Android L之前,我們可以在startActivity之後調用overridePendingTransition來指定Activity的轉場動畫。現在Android L給我們帶來了更絢麗的轉場動畫。
新的轉場動畫分爲兩大類,一種是普通的過渡動畫,另一種是共享元素的過渡動畫。 要想使用新的轉場動畫,可以繼承Material Design主題後在style風格中指定:
<style name="DefaultTheme" parent="android:Theme.Material">
<!-- 允許使用transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- 指定進入、退出、返回、重新進入時的transitions -->
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>
<item name="android:windowReturnTransition">@transition/explode</item>
<item name="android:windowReenterTransition">@transition/explode</item>
<!-- 指定進入、退出、返回、重新進入時的共享transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/change</item>
<item name="android:windowSharedElementExitTransition">@transition/change</item>
<item name="android:windowSharedElementReturnTransition">@transition/change</item>
<item name="android:windowSharedElementReenterTransition">@transition/change</item>
</style>
也可以在activity的oncreate方法中進行代碼設置:
// 允許使用transitions
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
// 指定進入、退出、返回、重新進入時的transitions
getWindow().setEnterTransition(new Explode());
getWindow().setExitTransition(new Explode());
getWindow().setEnterTransition(new Explode());
getWindow().setExitTransition(new Explode());
// 指定進入、退出、返回、重新進入時的共享transitions
getWindow().setSharedElementEnterTransition(new ChangeTransform());
getWindow().setSharedElementExitTransition(new ChangeTransform());
getWindow().setSharedElementReturnTransition(new ChangeTransform());
getWindow().setSharedElementReenterTransition(new ChangeTransform());
普通轉場動畫
所有繼承自visibility類都可以作爲進入、退出的過度動畫。如果我們想自定義進入和退出時的動畫效果,只需要繼承Visibility,重載onAppear和onDisappear方法來定義進入喝退出的動畫。系統提供了三種默認方式:
- explode 從屏幕中心移入或移出視圖
- slide 從屏幕邊緣移入或移出視圖
- fade 改變視圖的透明度
想在xml中指定自定義的進入、退出的過度動畫需要先對動畫進行定義:
<transition class="my.app.transition.CustomTransition"/>
注意:其中CustomTransition是你自定義的動畫,它必須繼承自Visibility。
想以普通轉場動畫的方式啓動一個Activity,必須在startActivity函數中傳遞一個ActivityOptions的Bundle對象:
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity);
startActivity(intent, options.toBundle());
如果想讓返回也具備轉場效果,那麼在返回的Activity中不要再調用finish
函數,而是應該使用finishAfterTransition
來結束一個Activity,該函數會等待動畫執行完畢才結束該Activity。
共享轉場動畫
當兩個Activity具備某些相遇的元素時,共享轉場動畫將是一個非常好的選擇。使用轉場動畫需要將相同的元素通過android:transitionName
或者view.setTransitionName
設置爲相同的名稱,這樣系統才能區分出相同的元素。
共享轉場動畫支持以下共享元素:
- changeBounds 對目標視圖的大小進行動畫
- changeClipBounds 對目標視圖的剪裁大小進行動畫
- changeTransform 對目標視圖進行縮放、旋轉、位移動畫
- changeImageTransform 對目標圖片進行縮放
通過下面的函數啓動一個共享元素動畫:
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, view,
"name");
startActivity(intent, options.toBundle());
如果有多個共享元素,則可以通過Pair進行包裝處理:
ActivityOptions options =
ActivityOptions.makeSceneTransitionAnimation(activity,Pair.create(view1,
"name1"),Pair.create(view2, "name2"));
startActivity(intent,.toBundle());
返回時如果需要具備轉場動畫,那麼也需要用finish
函數替代finishAfterTransition
來結束一個Activity。
共享轉場動畫通常可以根據指定的元素判斷出合適的轉場動畫效果,不需要我們做額外的處理,也可以通過之前學習的方法進行指定共享元素轉場動畫效果。
組合轉場動畫
我們可以把多個轉場動畫進行組合,作出更具個性的轉場效果,在資源文件中通過以下方式:
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<explode/>
<transition class="my.app.transition.CustomTransition"/>
<<changeImageTransform/>
</transitionSet>
代碼中我們可以通過TransitionSet
類組合多個轉場動畫:
TransitionSet transitionSet = new TransitionSet();
transitionSet.addTransition(new Fade());
transitionSet.addTransition(new ChangeBounds());
組合可以同時針對普通轉場動畫和共享元素轉場動畫。
轉場動畫也可以像普通動畫一樣設置持續時間,延期執行時間,速率插入器,以及動畫的監聽等。
轉場動畫通常是對整個佈局起作用,如果我們想對某個特定的view實施轉場動畫,可以把該view設置爲轉場動畫的target,這樣轉場動畫將只對特定的view起作用。共享元素的動畫的target需要指定爲transitionName