在上一篇文章中,我们实现了界面的展现。如果你还没读过,请点击下面的链接:

http://www.cnblogs.com/fuly550871915/p/4930470.html

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

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

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

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

        修改ArcMenu中的代码如下:

  1 package com.example.menu;
  2 
  3 import android.content.Context;
  4 import android.content.res.TypedArray;
  5 import android.util.AttributeSet;
  6 import android.util.TypedValue;
  7 import android.view.View;
  8 import android.view.View.OnClickListener;
  9 import android.view.animation.Animation;
 10 import android.view.animation.Animation.AnimationListener;
 11 import android.view.animation.AnimationSet;
 12 import android.view.animation.RotateAnimation;
 13 import android.view.animation.TranslateAnimation;
 14 import android.view.ViewGroup;
 15 
 16 public class ArcMenu extends ViewGroup implements OnClickListener{
 17     /**
 18      * 菜单按钮
 19      */
 20     private View mCBMenu;
 21     /**
 22      * 菜单的位置,为枚举类型
 23      * @author fuly1314
 24      *
 25      */
 26     private enum Position
 27     {
 28         LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM
 29     }
 30     /**
 31      * 菜单的状态
 32      * @author fuly1314
 33      *
 34      */
 35     private enum Status
 36     {
 37         OPEN,CLOSE
 38     }
 39     /**
 40      * 菜单为当前位置,默认为RIGHT_BOTTOM,在后面我们可以获取到
 41      */
 42     private Position mPosition = Position.RIGHT_BOTTOM;
 43     /**
 44      * 菜单的当前状态,默认为关闭
 45      */
 46     private Status mCurStatus = Status.CLOSE;
 47     
 48     /**
 49      * 菜单的半径,默认为120dp
 50      */
 51     private int mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150,
 52             getResources().getDisplayMetrics());
 53 
 54     
 55     
 56     public ArcMenu(Context context) {
 57         this(context,null);
 58     }
 59     public ArcMenu(Context context, AttributeSet attrs) {
 60         this(context,attrs,0);
 61     }
 62     public ArcMenu(Context context, AttributeSet attrs, int defStyle) {
 63         super(context, attrs, defStyle);
 64         
 65         TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ArcMenu, defStyle, 0);
 66         //获取到菜单设置的位置
 67         int position = ta.getInt(R.styleable.ArcMenu_position, 3);
 68         
 69         switch(position){
 70         case 0:
 71             mPosition = Position.LEFT_TOP;
 72             break;
 73         case 1:
 74             mPosition = Position.LEFT_BOTTOM;
 75             break;
 76         case 2:
 77             mPosition = Position.RIGHT_TOP;
 78             break;
 79         case 3:
 80             mPosition = Position.RIGHT_BOTTOM;
 81             break;
 82         }
 83         
 84         //获取到菜单的半径
 85         mRadius = (int) ta.getDimension(R.styleable.ArcMenu_radius,
 86                 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120,
 87                         getResources().getDisplayMetrics()));            
 88         ta.recycle();
 89         
 90     }
 91     
 92     
 93     
 94     /**
 95      * 测量各个子View的大小
 96      */
 97     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
 98     {
 99         int count = getChildCount();//获取子view的数量
100         
101         for(int i=0;i<count;i++)
102         {
103             //测量子view的大小
104             measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
105         }
106         
107         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
108     }
109 
110     /**
111      * 摆放各个子view的位置
112      */
113     protected void onLayout(boolean changed, int l, int t, int r, int b) {
114         
115         if(changed)//如果发生了改变,就重新布局
116         {
117             layoutMainMenu();//菜单按钮的布局
118             /**
119              * 下面的代码为菜单的布局
120              */
121             int count = getChildCount();
122             
123             for(int i=0;i<count-1;i++)
124             {
125                 View childView = getChildAt(i+1);//注意这里过滤掉菜单按钮,只要菜单选项view
126                 
127                 childView.setVisibility(GONE);//先让菜单消失
128                 
129                 int left = (int) (mRadius*Math.cos(Math.PI/2/(count-2)*i));
130                 int top = (int) (mRadius*Math.sin(Math.PI/2/(count-2)*i));
131 
132                 
133                 
134                 switch(mPosition)
135                 {
136                 
137                 case LEFT_TOP:
138                     break;
139                 case LEFT_BOTTOM:
140                     top = getMeasuredHeight() - top-childView.getMeasuredHeight();
141                     break;
142                 case RIGHT_TOP:
143                     left = getMeasuredWidth() - left-childView.getMeasuredWidth();
144                     break;
145                 case RIGHT_BOTTOM:
146                     left = getMeasuredWidth() - left-childView.getMeasuredWidth();
147                     top = getMeasuredHeight() - top-childView.getMeasuredHeight();
148                     break;
149                 }
150                 
151                 childView.layout(left, top, left+childView.getMeasuredWidth(),
152                         top+childView.getMeasuredHeight());
153             }
154         }
155 
156         
157     }
158     /**
159      * 菜单按钮的布局
160      */
161     private void layoutMainMenu() {
162         
163          mCBMenu = getChildAt(0);//获得主菜单按钮
164          
165          mCBMenu.setOnClickListener(this);
166         
167         int left=0;
168         int top=0;
169         
170         switch(mPosition)
171         {
172         case LEFT_TOP:
173             left = 0;
174             top = 0;
175             break;
176         case LEFT_BOTTOM:
177             left = 0;
178             top = getMeasuredHeight() - mCBMenu.getMeasuredHeight();
179             break;
180         case RIGHT_TOP:
181             left = getMeasuredWidth() - mCBMenu.getMeasuredWidth();
182             top = 0;
183             break;
184         case RIGHT_BOTTOM:
185             left = getMeasuredWidth() - mCBMenu.getMeasuredWidth();
186             top = getMeasuredHeight() - mCBMenu.getMeasuredHeight();
187             break;
188         }
189         
190         mCBMenu.layout(left, top, left+mCBMenu.getMeasuredWidth(), top+mCBMenu.getMeasuredHeight());
191     }
192     /**
193      * 菜单按钮的点击事件
194      * @param v
195      */
196     public void onClick(View v) {
197         //为菜单按钮设置点击动画
198         RotateAnimation rAnimation = new RotateAnimation(0f, 720f, Animation.RELATIVE_TO_SELF, 0.5f, 
199                 Animation.RELATIVE_TO_SELF, 0.5f);
200         
201         rAnimation.setDuration(300);
202         
203         rAnimation.setFillAfter(true);
204         
205         v.startAnimation(rAnimation);
206         
207         dealChildMenu(300);//处理菜单选项,比如折叠菜单或者展开菜单
208         
209     }
210     /**
211      * 处理菜单选项,比如折叠菜单或者展开菜单
212      * @param duration 菜单选项的动画时间
213      */
214     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     }
314 
315 }

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

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

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

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

      哈哈,还不错吧。快接近我们的目标了吧。下面我们就为每一个菜单添加点击动画

posted on 2015-11-02 16:52  fuly  阅读(785)  评论(0编辑  收藏  举报