在上一篇文章中,我们实现了界面的展现。如果你还没读过,请点击下面的链接:
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方向平移的值,是正是负,你可以一一分析,然后找出其中规律。在上面的代码中,已经写出来了。我就不再多说了。关键在于自己分析。
然后运行程序,看看效果吧。如下:
哈哈,还不错吧。快接近我们的目标了吧。下面我们就为每一个菜单添加点击动画