最近真的是忙死了,做了很久的這個菜單動畫特效,都沒有時間寫博客,今天在機場等飛機終於有了空閑時間。
上圖先:
那么下面開始吧~
首先,將整個菜單動畫分解開來。
1. 一級菜單按鈕的旋轉動畫2個,十字和叉叉狀態的轉換。
2. 二級菜單按鈕的平移動畫2個,彈簧效果的in和out
3. 二級菜單按鈕的點擊效果,放大消失,其他未點擊按鈕縮小消失。
4. 一級菜單按鈕的恢復效果,放大出現
好的 逐一去實現:
首先是一級菜單按鈕的旋轉動畫,這2個動畫可以直接在xml中定義,然后load到代碼中來,具體代碼如下:
rotate_story_add_button_in.xml
2 < rotate
3 xmlns:android ="http://schemas.android.com/apk/res/android"
4 android:interpolator ="@android:anim/linear_interpolator"
5 android:duration ="150"
6 android:fromDegrees ="0.0"
7 android:toDegrees ="-225.0"
8 android:pivotX ="50.0%"
9 android:pivotY ="50.0%"
10 android:fillAfter ="true"
11 android:fillEnabled ="true"
12 />
rotate_story_add_button_out.xml
2 < rotate
3 xmlns:android ="http://schemas.android.com/apk/res/android"
4 android:interpolator ="@android:anim/linear_interpolator"
5 android:duration ="150"
6 android:fromDegrees ="-225.0"
7 android:toDegrees ="0.0"
8 android:pivotX ="50.0%"
9 android:pivotY ="50.0%"
10 android:fillAfter ="true"
11 android:fillEnabled ="true"
12 />
這2段沒什么好說的,定義好角度即可。
接下來是需要我們在代碼中定義的動畫部分,這幾個動畫的部分需要定義一個基類,作為統一的調用接口,這個基類被稱作InOutAnimation,繼承自AnimationSet,這個基類的主要工作是為view提供in和out兩種不同的狀態時的動畫效果。其子類需要實現2個方法:
2 protected abstract void addOutAnimation(View aview[]);
從而進行view的入場和離場動畫。
下面是InOutAnimation的代碼部分:
2
3 public Direction direction;
4
5 public enum Direction {
6 IN, OUT;
7 }
8
9 public InOutAnimation(Direction direction, long l, View[] aview) {
10 super( true);
11 this.direction = direction;
12 switch ( this.direction) {
13 case IN:
14 addInAnimation(aview);
15 break;
16 case OUT:
17 addOutAnimation(aview);
18
19 break;
20 }
21 setDuration(l);
22 }
23
24 protected abstract void addInAnimation(View aview[]);
25
26 protected abstract void addOutAnimation(View aview[]);
27
28 }
接下來就是重頭戲啦,二級菜單按鈕的平移動畫。
這部分動畫看起來可能會比較復雜和神秘,其實不然,當把整個動畫過程分解開來的時候,都是最最簡單的平移而已,我們要做的只是定義一下平移的起點和終點、開始動畫的順序以及插值(Interpolator),讓整個過程看起來很炫。
先說動畫的起點和終點吧,起點很簡單,就是整個view的左下角,即0,0點,為了效果漂亮一些,我們稍微的將左下角位置定義的有一些偏移,經驗上的值是16,-13,這個點的位置看你心情而定咯~ 好 終點就是你想讓他在的點上就好了,終點我們將定義到layout中去,為這個2級菜單指定一個margin的值就好。
還是上代碼比較直觀:
動畫如下:
2 擴張部分:TranslateAnimation(0F, xOffset + -mlp.leftMargin, 0F,yOffset + mlp.bottomMargin)
位置定義部分:
例如:
android:layout_marginBottom="142dp" android:layout_marginLeft="10.667dp"
這個位置大家可以直觀的在布局文件中看到,詳細的布局文件也將在下面展示。
以上是單獨的每一個二級按鈕的動畫,而組合的動畫就是指定了一下開始的時間差以及插值:
這個就是奧妙所在了,OvershootInterpolator AnticipateInterpolator 這2個插值器提供了彈力效果。
整段的代碼如下:
ComposerButtonAnimation.java
2
3 public static final int DURATION = 500;
4 private static final int xOffset = 16;
5 private static final int yOffset = -13;
6
7 public ComposerButtonAnimation(Direction direction, long l, View view) {
8 super(direction, l, new View[] { view });
9 }
10
11 public static void startAnimations(ViewGroup viewgroup,
12 InOutAnimation.Direction direction) {
13 switch (direction) {
14 case IN:
15 startAnimationsIn(viewgroup);
16 break;
17 case OUT:
18 startAnimationsOut(viewgroup);
19 break;
20 }
21 }
22
23 private static void startAnimationsIn(ViewGroup viewgroup) {
24 for ( int i = 0; i < viewgroup.getChildCount(); i++) {
25 if (viewgroup.getChildAt(i) instanceof InOutImageButton) {
26 InOutImageButton inoutimagebutton = (InOutImageButton) viewgroup
27 .getChildAt(i);
28 ComposerButtonAnimation animation = new ComposerButtonAnimation(
29 InOutAnimation.Direction.IN, DURATION, inoutimagebutton);
30 animation.setStartOffset((i * 100)
31 / (-1 + viewgroup.getChildCount()));
32 animation.setInterpolator( new OvershootInterpolator(2F));
33 inoutimagebutton.startAnimation(animation);
34 }
35 }
36 }
37
38 private static void startAnimationsOut(ViewGroup viewgroup) {
39 for ( int i = 0; i < viewgroup.getChildCount(); i++) {
40 if (viewgroup.getChildAt(i) instanceof InOutImageButton) {
41 InOutImageButton inoutimagebutton = (InOutImageButton) viewgroup
42 .getChildAt(i);
43 ComposerButtonAnimation animation = new ComposerButtonAnimation(
44 InOutAnimation.Direction.OUT, DURATION,
45 inoutimagebutton);
46 animation.setStartOffset((100 * ((-1 + viewgroup
47 .getChildCount()) - i))
48 / (-1 + viewgroup.getChildCount()));
49 animation.setInterpolator( new AnticipateInterpolator(2F));
50 inoutimagebutton.startAnimation(animation);
51 }
52 }
53 }
54
55 @Override
56 protected void addInAnimation(View[] aview) {
57 MarginLayoutParams mlp = (MarginLayoutParams) aview[0]
58 .getLayoutParams();
59 addAnimation( new TranslateAnimation(xOffset + -mlp.leftMargin, 0F,
60 yOffset + mlp.bottomMargin, 0F));
61 }
62
63 @Override
64 protected void addOutAnimation(View[] aview) {
65 MarginLayoutParams mlp = (MarginLayoutParams) aview[0]
66 .getLayoutParams();
67 addAnimation( new TranslateAnimation(0F, xOffset + -mlp.leftMargin, 0F,
68 yOffset + mlp.bottomMargin));
69 }
70 }
剩下的增大出現、增大消失及縮小消失都是scale和alpha的組合動畫
例如增大出現為:
2 addAnimation( new AlphaAnimation(0F, 1F));
整段的代碼如下:
2
3 public ComposerButtonGrowAnimationIn( int i) {
4 super(InOutAnimation.Direction.IN, i, new View[0]);
5 }
6
7 @Override
8 protected void addInAnimation(View[] aview) {
9 addAnimation( new ScaleAnimation(0F, 1F, 0F, 1F, 1, 0.5F, 1, 0.5F));
10 addAnimation( new AlphaAnimation(0F, 1F));
11
12 }
13
14 @Override
15 protected void addOutAnimation(View[] aview) {}
16
17 }
18
19 public class ComposerButtonGrowAnimationOut extends InOutAnimation {
20
21 public ComposerButtonGrowAnimationOut( int i) {
22 super(InOutAnimation.Direction.OUT, i, new View[0]);
23 }
24
25 @Override
26 protected void addInAnimation(View[] aview) {}
27
28 @Override
29 protected void addOutAnimation(View[] aview) {
30 addAnimation( new ScaleAnimation(1F, 5F, 1F, 5F, 1, 0.5F, 1, 0.5F));
31 addAnimation( new AlphaAnimation(1F, 0F));
32 }
33
34 } public class ComposerButtonShrinkAnimationOut extends InOutAnimation {
35
36 public ComposerButtonShrinkAnimationOut( int i) {
37 super(InOutAnimation.Direction.OUT, i, new View[0]);
38 }
39
40 @Override
41 protected void addInAnimation(View[] aview) {
42
43 }
44
45 @Override
46 protected void addOutAnimation(View[] aview) {
47 addAnimation( new ScaleAnimation(1F, 0F, 1F, 0F, 1, 0.5F, 1, 0.5F));
48 addAnimation( new AlphaAnimation(1F, 0F));
49 }
50
51 }
接下來我們需要為這些控件做一下擴展,以便其可以再動畫完成后顯示或消失。
很簡單
2
3 private Animation animation;
4
5 public InOutImageButton(Context context, AttributeSet attrs, int defStyle) {
6 super(context, attrs, defStyle);
7 }
8
9 public InOutImageButton(Context context, AttributeSet attrs) {
10 super(context, attrs);
11 }
12
13 public InOutImageButton(Context context) {
14 super(context);
15 }
16
17 @Override
18 protected void onAnimationEnd() {
19 super.onAnimationEnd();
20 if (( this.animation instanceof InOutAnimation)) {
21 setVisibility(((InOutAnimation) this.animation).direction != InOutAnimation.Direction.OUT ? View.VISIBLE
22 : View.GONE);
23 }
24 }
25
26 @Override
27 protected void onAnimationStart() {
28 super.onAnimationStart();
29 if (( this.animation instanceof InOutAnimation))
30 setVisibility(View.VISIBLE);
31 }
32
33 @Override
34 public void startAnimation(Animation animation) {
35 super.startAnimation(animation);
36 this.animation = animation;
37 getRootView().postInvalidate();
38 }
39 }
那么到這里基本上就已經搞定了所有的事情了,剩下點沒做的事就是把這些動畫效果設置給對應的控件了,這里就不詳細描述了,大家有興趣可以直接下載源碼來搞一下啦~