您的位置:首页 > 其它

(二)实现菜单动画

2015-11-02 16:52 471 查看
在上一篇文章中,我们实现了界面的展现。如果你还没读过,请点击下面的链接:

/article/6123632.html

贴一张上一篇文章实现的效果图吧,如下:



虽然我们将各个菜单都全部展现了出来,但是菜单的动画以及菜单的点击都还没有实现。在上一篇文章我们也分析了,如果将菜单的位置设定在红色按钮那里,然后给菜单设定补间动画,让其移动到图示位置,虽然动画实现了,会影响到点击事件。而属性动画虽然可以解决这个问题,但是它的向下兼容性不是很好,因此我们依旧坚持使用补间动画。那么我们要怎样解决这个难题呢?思路如下:

我们让菜单的原本位置就为上图所示的位置,刚开始的时候让其不可见。然后当点击红色按钮的时候,我们给其设定动画,还是让其红色按钮移动到图示位置,然后让其可见。这样子,我们点击图示位置的按钮,就会有点击事件,就没什么影响了。其实就换一种角度,将其初始位置直接放在要移动的终点位置而不是起点位置。


那么这个菜单的动画包括什么呢?首先是平移动画,每一个菜单平移的x方向和y方向的距离,与上一篇中我们在计算其位置的时的坐标是一样的。因此没什么难度,稍微修改先即可。然后就是平移的同时去旋转,旋转也很容易实现,没什么好说的。下面我们看一看实际的代码吧,然后再做解释。

修改ArcMenu中的代码如下:

package com.example.menu;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.TranslateAnimation;
import android.view.ViewGroup;

public class ArcMenu extends ViewGroup implements OnClickListener{
/**
* 菜单按钮
*/
private View mCBMenu;
/**
* 菜单的位置,为枚举类型
* @author fuly1314
*
*/
private enum Position
{
LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM
}
/**
* 菜单的状态
* @author fuly1314
*
*/
private enum Status
{
OPEN,CLOSE
}
/**
* 菜单为当前位置,默认为RIGHT_BOTTOM,在后面我们可以获取到
*/
private Position mPosition = Position.RIGHT_BOTTOM;
/**
* 菜单的当前状态,默认为关闭
*/
private Status mCurStatus = Status.CLOSE;

/**
* 菜单的半径,默认为120dp
*/
private int mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150,
getResources().getDisplayMetrics());

public ArcMenu(Context context) {
this(context,null);
}
public ArcMenu(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public ArcMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ArcMenu, defStyle, 0);
//获取到菜单设置的位置
int position = ta.getInt(R.styleable.ArcMenu_position, 3);

switch(position){
case 0:
mPosition = Position.LEFT_TOP;
break;
case 1:
mPosition = Position.LEFT_BOTTOM;
break;
case 2:
mPosition = Position.RIGHT_TOP;
break;
case 3:
mPosition = Position.RIGHT_BOTTOM;
break;
}

//获取到菜单的半径
mRadius = (int) ta.getDimension(R.styleable.ArcMenu_radius,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120,
getResources().getDisplayMetrics()));
ta.recycle();

}

/**
* 测量各个子View的大小
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int count = getChildCount();//获取子view的数量

for(int i=0;i<count;i++)
{
//测量子view的大小
measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
}

super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

/**
* 摆放各个子view的位置
*/
protected void onLayout(boolean changed, int l, int t, int r, int b) {

if(changed)//如果发生了改变,就重新布局
{
layoutMainMenu();//菜单按钮的布局
/**
* 下面的代码为菜单的布局
*/
int count = getChildCount();

for(int i=0;i<count-1;i++)
{
View childView = getChildAt(i+1);//注意这里过滤掉菜单按钮,只要菜单选项view

childView.setVisibility(GONE);//先让菜单消失

int left = (int) (mRadius*Math.cos(Math.PI/2/(count-2)*i));
int top = (int) (mRadius*Math.sin(Math.PI/2/(count-2)*i));

switch(mPosition)
{

case LEFT_TOP:
break;
case LEFT_BOTTOM:
top = getMeasuredHeight() - top-childView.getMeasuredHeight();
break;
case RIGHT_TOP:
left = getMeasuredWidth() - left-childView.getMeasuredWidth();
break;
case RIGHT_BOTTOM:
left = getMeasuredWidth() - left-childView.getMeasuredWidth();
top = getMeasuredHeight() - top-childView.getMeasuredHeight();
break;
}

childView.layout(left, top, left+childView.getMeasuredWidth(),
top+childView.getMeasuredHeight());
}
}

}
/**
* 菜单按钮的布局
*/
private void layoutMainMenu() {

mCBMenu = getChildAt(0);//获得主菜单按钮

mCBMenu.setOnClickListener(this);

int left=0;
int top=0;

switch(mPosition)
{
case LEFT_TOP:
left = 0;
top = 0;
break;
case LEFT_BOTTOM:
left = 0;
top = getMeasuredHeight() - mCBMenu.getMeasuredHeight();
break;
case RIGHT_TOP:
left = getMeasuredWidth() - mCBMenu.getMeasuredWidth();
top = 0;
break;
case RIGHT_BOTTOM:
left = getMeasuredWidth() - mCBMenu.getMeasuredWidth();
top = getMeasuredHeight() - mCBMenu.getMeasuredHeight();
break;
}

mCBMenu.layout(left, top, left+mCBMenu.getMeasuredWidth(), top+mCBMenu.getMeasuredHeight());
}
/**
* 菜单按钮的点击事件
* @param v
*/
public void onClick(View v) {
//为菜单按钮设置点击动画
RotateAnimation rAnimation = new RotateAnimation(0f, 720f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);

rAnimation.setDuration(300);

rAnimation.setFillAfter(true);

v.startAnimation(rAnimation);

dealChildMenu(300);//处理菜单选项,比如折叠菜单或者展开菜单

}
/**
* 处理菜单选项,比如折叠菜单或者展开菜单
* @param duration 菜单选项的动画时间
*/
private void dealChildMenu(int duration)
215     {
216
217         //下面的代码为菜单选项设置动画
218
219         int count = getChildCount();
220
221         for(int i=0;i<count-1;i++)
222         {
223             final View childView = getChildAt(i+1);
224
225             AnimationSet set = new AnimationSet(true);
226
227             //1.首先是平移动画
228             TranslateAnimation tAnimation = null;
229
230             //平移的x方向和y方向的距离
231             int x = (int) (mRadius*Math.cos(Math.PI/2/(count-2)*i));
232             int y = (int) (mRadius*Math.sin(Math.PI/2/(count-2)*i));
233
234
235
236
237             //平移的标志,是平移一个正数还以一个负数
238             int xflag =1;
239             int yflag =1;
240
241             if(mPosition == Position.LEFT_TOP||mPosition == Position.LEFT_BOTTOM)
242             {
243                 xflag = -1;
244             }
245             if(mPosition == Position.LEFT_TOP||mPosition == Position.RIGHT_TOP)
246             {
247                 yflag = -1;
248             }
249
250             if(mCurStatus == Status.CLOSE)//如果当前状态为关闭则应该打开
251             {
252                  tAnimation = new TranslateAnimation(xflag*x, 0,
253                         yflag*y, 0);
254                 tAnimation.setDuration(duration);
255                 tAnimation.setFillAfter(true);
256
257                 childView.setVisibility(VISIBLE);
258
259             }else//否则为打开状态,就应该关闭
260             {
261                  tAnimation = new TranslateAnimation( 0,xflag*x,
262                             0,yflag*y);
263                     tAnimation.setDuration(duration);
264                     tAnimation.setFillAfter(true);
265             }
266             tAnimation.setStartOffset((i * 100) / count);
267             tAnimation.setAnimationListener(new AnimationListener() {
268
269
270                 public void onAnimationStart(Animation animation) {
271
272
273                 }
274
275
276                 public void onAnimationRepeat(Animation animation) {
277
278
279                 }
280
281
282                 public void onAnimationEnd(Animation animation) {
283
284                     if(mCurStatus == Status.CLOSE)
285                     childView.setVisibility(GONE);
286                 }
287             });
288
289             //2.然后是旋转动画
290             RotateAnimation rAnimation = new RotateAnimation(0f, 0, Animation.RELATIVE_TO_SELF, 0.5f,
291                     Animation.RELATIVE_TO_SELF, 0.5f);
292             rAnimation.setDuration(duration);
293             rAnimation.setFillAfter(true);//动画结束是画面停留在此动画的最后一帧
294
295
296             set.addAnimation(rAnimation);//一定要注意顺序,先旋转动画,然后再平移
297             set.addAnimation(tAnimation);
298
299             childView.startAnimation(set);
300
301         }
302
303         changeStatus();//动画完成后,要改变状态
304
305     }
306     /**
307      * 改变状态
308      */
309     private void changeStatus() {
310
311         mCurStatus = (mCurStatus == Status.CLOSE?Status.OPEN:Status.CLOSE);
312
313     }

}


注意红色部分,是我们主要添加的代码。我们要求点击红色按钮的时候,展开菜单或者关闭菜单。因此,肯定在其点击事件里,调用处理菜单操作的方法了,即dealChildMenu(300);方法。在这个方法里,首先为每一个菜单设置平移动画,然后再为其设置旋转动画。注意,平移动画中,平移的距离,与红色按钮的方位有关,因此我们设置了xflag和yflag来标志在不同的方位,平移的距离的应该是一个正数还是一个负数。再就是点击红色按钮,是该展开菜单呢还是该折叠菜单,这与当前的状态有关。如果当前为展开状态,那么点击就应该关闭,当前为关闭状态,那么点击就应该展开。

如果你对平移距离不是很懂,那么我建议你结合上面的代码,用笔在本子上好好画一画,分析分析。在这里我姑且就分析x方向的一种情况吧,作为一个引子。如下两张图:



A是红色按钮,B是其中一个菜单。如左图的情况,根据我们的动画要求,B的位置一直就在图示位置,但是我们要用平移动画,让其从A点移动过来。那么A的x位置相对于B的x位置来说,不就是一个正值嘛。如果是右图的情况,那么A的x位置相对于B的x位置不就是一个负值嘛。因此x方向平移和y方向平移的值,是正是负,你可以一一分析,然后找出其中规律。在上面的代码中,已经写出来了。我就不再多说了。关键在于自己分析。

然后运行程序,看看效果吧。如下:



哈哈,还不错吧。快接近我们的目标了吧。下面我们就为每一个菜单添加点击动画
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: