上图先:
那么下面开始吧~
首先,将整个菜单动画分解开来。
1. 一级菜单按钮的旋转动画2个,十字和叉叉状态的转换。
2. 二级菜单按钮的平移动画2个,弹簧效果的in和out
3. 二级菜单按钮的点击效果,放大消失,其他未点击按钮缩小消失。
4. 一级菜单按钮的恢复效果,放大出现
好的 逐一去实现:
首先是一级菜单按钮的旋转动画,这2个动画可以直接在xml中定义,然后load到代码中来,具体代码如下:
rotate_story_add_button_in.xml
1
<?
xml version="1.0" encoding="UTF-8"
?>
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
1
<?
xml version="1.0" encoding="UTF-8"
?>
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个方法:
1
protected
abstract
void addInAnimation(View aview[]);
2
protected
abstract
void addOutAnimation(View aview[]);
从而进行view的入场和离场动画。
下面是InOutAnimation的代码部分:
1
public
abstract
class InOutAnimation
extends AnimationSet {
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的值就好。
还是上代码比较直观:
动画如下:
1 收缩部分:TranslateAnimation(xOffset + -mlp.leftMargin, 0F,yOffset + mlp.bottomMargin, 0F)
2 扩张部分:TranslateAnimation(0F, xOffset + -mlp.leftMargin, 0F,yOffset + mlp.bottomMargin)
位置定义部分:
例如:
android:layout_marginBottom="142dp" android:layout_marginLeft="10.667dp"
这个位置大家可以直观的在布局文件中看到,详细的布局文件也将在下面展示。
以上是单独的每一个二级按钮的动画,而组合的动画就是指定了一下开始的时间差以及插值:
这个就是奥妙所在了,OvershootInterpolator AnticipateInterpolator 这2个插值器提供了弹力效果。
整段的代码如下:
ComposerButtonAnimation.java
1
public
class ComposerButtonAnimation
extends InOutAnimation {
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的组合动画
例如增大出现为:
1 addAnimation(
new ScaleAnimation(0F, 1F, 0F, 1F, 1, 0.5F, 1, 0.5F));
2 addAnimation(
new AlphaAnimation(0F, 1F));
整段的代码如下:
1
public
class ComposerButtonGrowAnimationIn
extends InOutAnimation {
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 }
接下来我们需要为这些控件做一下扩展,以便其可以再动画完成后显示或消失。
很简单
1
public
class InOutImageButton
extends ImageButton {
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 }
那么到这里基本上就已经搞定了所有的事情了,剩下点没做的事就是把这些动画效果设置给对应的控件了,这里就不详细描述了。