在上一篇文章中,我们给菜单的点击设置了动画,如果你没有阅读过,可以单击下面的链接:
贴出上一篇文章实现的效果图吧。如下:
下面我们要实现的逻辑也很简单,点击菜单,弹出一个提示框。怎么实现呢,只需要在ArcMenu中提供一个回调接口即可。这样子,我们就可以在MainActivity中重写回调方法,然后再ArcMenu中点击菜单时调用即可。
修改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.AlphaAnimation;
12 import android.view.animation.AnimationSet;
13 import android.view.animation.RotateAnimation;
14 import android.view.animation.ScaleAnimation;
15 import android.view.animation.TranslateAnimation;
16 import android.view.ViewGroup;
17
18 public class ArcMenu extends ViewGroup implements OnClickListener{
19 /**
20 * 菜单按钮
21 */
22 private View mCBMenu;
23 /**
24 * 菜单的位置,为枚举类型
25 * @author fuly1314
26 *
27 */
28 private enum Position
29 {
30 LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM
31 }
32 /**
33 * 菜单的状态
34 * @author fuly1314
35 *
36 */
37 private enum Status
38 {
39 OPEN,CLOSE
40 }
41 /**
42 * 菜单为当前位置,默认为RIGHT_BOTTOM,在后面我们可以获取到
43 */
44 private Position mPosition = Position.RIGHT_BOTTOM;
45 /**
46 * 菜单的当前状态,默认为关闭
47 */
48 private Status mCurStatus = Status.CLOSE;
49
50 /**
51 * 菜单的半径,默认为120dp
52 */
53
54 /**
55 * 提供一个回调接口,用来处理菜单的点击事件,点击后需要处理的事情
56 */
57 public interface ArcMenuListener
58 {
59 void dealMenuClick(View v);
60 }
61 public void setOnArcMenuListener(ArcMenuListener listener){
62
63 mListener = listener;
64 }
65 private ArcMenuListener mListener;
66
67
68
69 private int mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150,
70 getResources().getDisplayMetrics());
71
72
73
74 public ArcMenu(Context context) {
75 this(context,null);
76 }
77 public ArcMenu(Context context, AttributeSet attrs) {
78 this(context,attrs,0);
79 }
80 public ArcMenu(Context context, AttributeSet attrs, int defStyle) {
81 super(context, attrs, defStyle);
82
83 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ArcMenu, defStyle, 0);
84 //获取到菜单设置的位置
85 int position = ta.getInt(R.styleable.ArcMenu_position, 3);
86
87 switch(position){
88 case 0:
89 mPosition = Position.LEFT_TOP;
90 break;
91 case 1:
92 mPosition = Position.LEFT_BOTTOM;
93 break;
94 case 2:
95 mPosition = Position.RIGHT_TOP;
96 break;
97 case 3:
98 mPosition = Position.RIGHT_BOTTOM;
99 break;
100 }
101
102 //获取到菜单的半径
103 mRadius = (int) ta.getDimension(R.styleable.ArcMenu_radius,
104 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120,
105 getResources().getDisplayMetrics()));
106 ta.recycle();
107
108 }
109
110
111
112 /**
113 * 测量各个子View的大小
114 */
115 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
116 {
117 int count = getChildCount();//获取子view的数量
118
119 for(int i=0;i<count;i++)
120 {
121 //测量子view的大小
122 measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
123 }
124
125 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
126 }
127
128 /**
129 * 摆放各个子view的位置
130 */
131 protected void onLayout(boolean changed, int l, int t, int r, int b) {
132
133 if(changed)//如果发生了改变,就重新布局
134 {
135 layoutMainMenu();//菜单按钮的布局
136 /**
137 * 下面的代码为菜单的布局
138 */
139 int count = getChildCount();
140
141 for(int i=0;i<count-1;i++)
142 {
143 View childView = getChildAt(i+1);//注意这里过滤掉菜单按钮,只要菜单选项view
144
145 childView.setVisibility(GONE);//先让菜单消失
146
147 int left = (int) (mRadius*Math.cos(Math.PI/2/(count-2)*i));
148 int top = (int) (mRadius*Math.sin(Math.PI/2/(count-2)*i));
149
150
151
152 switch(mPosition)
153 {
154
155 case LEFT_TOP:
156 break;
157 case LEFT_BOTTOM:
158 top = getMeasuredHeight() - top-childView.getMeasuredHeight();
159 break;
160 case RIGHT_TOP:
161 left = getMeasuredWidth() - left-childView.getMeasuredWidth();
162 break;
163 case RIGHT_BOTTOM:
164 left = getMeasuredWidth() - left-childView.getMeasuredWidth();
165 top = getMeasuredHeight() - top-childView.getMeasuredHeight();
166 break;
167 }
168
169 childView.layout(left, top, left+childView.getMeasuredWidth(),
170 top+childView.getMeasuredHeight());
171 }
172 }
173
174
175 }
176 /**
177 * 菜单按钮的布局
178 */
179 private void layoutMainMenu() {
180
181 mCBMenu = getChildAt(0);//获得主菜单按钮
182
183 mCBMenu.setOnClickListener(this);
184
185 int left=0;
186 int top=0;
187
188 switch(mPosition)
189 {
190 case LEFT_TOP:
191 left = 0;
192 top = 0;
193 break;
194 case LEFT_BOTTOM:
195 left = 0;
196 top = getMeasuredHeight() - mCBMenu.getMeasuredHeight();
197 break;
198 case RIGHT_TOP:
199 left = getMeasuredWidth() - mCBMenu.getMeasuredWidth();
200 top = 0;
201 break;
202 case RIGHT_BOTTOM:
203 left = getMeasuredWidth() - mCBMenu.getMeasuredWidth();
204 top = getMeasuredHeight() - mCBMenu.getMeasuredHeight();
205 break;
206 }
207
208 mCBMenu.layout(left, top, left+mCBMenu.getMeasuredWidth(), top+mCBMenu.getMeasuredHeight());
209 }
210 /**
211 * 菜单按钮的点击事件
212 * @param v
213 */
214 public void onClick(View v) {
215 //为菜单按钮设置点击动画
216 RotateAnimation rAnimation = new RotateAnimation(0f, 720f, Animation.RELATIVE_TO_SELF, 0.5f,
217 Animation.RELATIVE_TO_SELF, 0.5f);
218
219 rAnimation.setDuration(300);
220
221 rAnimation.setFillAfter(true);
222
223 v.startAnimation(rAnimation);
224
225 dealChildMenu(300);//处理菜单选项,比如折叠菜单或者展开菜单
226
227 }
228 /**
229 * 处理菜单选项,比如折叠菜单或者展开菜单
230 * @param duration 菜单选项的动画时间
231 */
232 private void dealChildMenu(int duration)
233 {
234
235 //下面的代码为菜单选项设置动画
236
237 int count = getChildCount();
238
239 for(int i=0;i<count-1;i++)
240 {
241 final View childView = getChildAt(i+1);
242
243 AnimationSet set = new AnimationSet(true);
244
245 //1.首先是平移动画
246 TranslateAnimation tAnimation = null;
247
248 //平移的x方向和y方向的距离
249 int x = (int) (mRadius*Math.cos(Math.PI/2/(count-2)*i));
250 int y = (int) (mRadius*Math.sin(Math.PI/2/(count-2)*i));
251
252
253
254
255 //平移的标志,是平移一个正数还以一个负数
256 int xflag =1;
257 int yflag =1;
258
259 if(mPosition == Position.LEFT_TOP||mPosition == Position.LEFT_BOTTOM)
260 {
261 xflag = -1;
262 }
263 if(mPosition == Position.LEFT_TOP||mPosition == Position.RIGHT_TOP)
264 {
265 yflag = -1;
266 }
267
268 if(mCurStatus == Status.CLOSE)//如果当前状态为关闭则应该打开
269 {
270 tAnimation = new TranslateAnimation(xflag*x, 0,
271 yflag*y, 0);
272 tAnimation.setDuration(duration);
273 tAnimation.setFillAfter(true);
274
275 }else//否则为打开状态,就应该关闭
276 {
277 tAnimation = new TranslateAnimation( 0,xflag*x,
278 0,yflag*y);
279 tAnimation.setDuration(duration);
280 tAnimation.setFillAfter(true);
281
282 }
283 tAnimation.setStartOffset((i * 100) / count);
284 tAnimation.setAnimationListener(new AnimationListener() {
285
286
287 public void onAnimationStart(Animation animation) {
288
289
290 }
291
292
293 public void onAnimationRepeat(Animation animation) {
294
295
296 }
297
298
299 public void onAnimationEnd(Animation animation) {
300
301 if(mCurStatus == Status.CLOSE)
302 {
303 childView.setVisibility(GONE);
304 childView.setClickable(false);
305 childView.setFocusable(false);
306 }
307 if(mCurStatus == Status.OPEN)
308 {
309 childView.setVisibility(VISIBLE);//设置菜单可见
310 //为打开状态,则菜单是可点击和获得焦点
311 childView.setClickable(true);
312 childView.setFocusable(true);
313 }
314
315 }
316 });
317
318 //2.然后是旋转动画
319 RotateAnimation rAnimation = new RotateAnimation(0f, 0, Animation.RELATIVE_TO_SELF, 0.5f,
320 Animation.RELATIVE_TO_SELF, 0.5f);
321 rAnimation.setDuration(duration);
322 rAnimation.setFillAfter(true);//动画结束是画面停留在此动画的最后一帧
323
324
325 set.addAnimation(rAnimation);//一定要注意顺序,先旋转动画,然后再平移
326 set.addAnimation(tAnimation);
327
328 childView.startAnimation(set);
329
330 //为菜单项设置点击事件
331 final int cPos = i+1;
332 childView.setOnClickListener(new OnClickListener() {
333
334 @Override
335 public void onClick(View v) {
336
337 clickAnimation(cPos);//点击动画
338
339 if(mListener != null)//处理点击事件的逻辑
340 {
341 mListener.dealMenuClick(childView);
342 }
343
344 changeStatus();
345
346
347 }
348 });
349
350
351 }
352
353 changeStatus();//动画完成后,要改变状态
354
355 }
356 /**
357 * 改变状态
358 */
359 private void changeStatus() {
360
361 mCurStatus = (mCurStatus == Status.CLOSE?Status.OPEN:Status.CLOSE);
362
363 }
364 /**
365 * 菜单项的点击动画
366 * @param cPos 用来判断当前点击的是哪一个菜单
367 */
368 private void clickAnimation(int cPos) {
369
370 for(int i=0;i<getChildCount()-1;i++)
371 {
372 View childView = getChildAt(i+1);
373
374 if(i+1== cPos)
375 {
376 AnimationSet set = new AnimationSet(true);
377 ScaleAnimation sAnimation = new ScaleAnimation(1.0f, 3.0f, 1.0f, 3.0f,
378 Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
379 sAnimation.setFillAfter(true);
380 AlphaAnimation alAnimation = new AlphaAnimation(1.0f, 0f);
381 alAnimation.setFillAfter(true);
382
383 set.addAnimation(sAnimation);
384 set.addAnimation(alAnimation);
385
386 set.setDuration(300);
387 childView.startAnimation(set);
388
389 }else
390 {
391 AnimationSet set = new AnimationSet(true);
392 ScaleAnimation sAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f,
393 Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);
394 sAnimation.setFillAfter(true);
395 AlphaAnimation alAnimation = new AlphaAnimation(1.0f, 0f);
396 alAnimation.setFillAfter(true);
397
398 set.addAnimation(sAnimation);
399 set.addAnimation(alAnimation);
400
401 set.setDuration(300);
402 childView.startAnimation(set);
403 }
404 childView.setVisibility(GONE);
405 }
406
407 }
408
409 }
红色部分是我们添加的主要代码。无非就是一个回调接口,没事什么好解释的了。
下面修改MainActivity中的代码即可,如下:
1 package com.example.menu;
2
3 import com.example.menu.ArcMenu.ArcMenuListener;
4
5 import android.os.Bundle;
6 import android.view.View;
7 import android.widget.Toast;
8 import android.app.Activity;
9
10 public class MainActivity extends Activity implements ArcMenuListener {
11
12 private ArcMenu menu;
13 @Override
14 protected void onCreate(Bundle savedInstanceState) {
15 super.onCreate(savedInstanceState);
16 setContentView(R.layout.activity_main);
17
18 menu = (ArcMenu) findViewById(R.id.id_menu);
19 menu.setOnArcMenuListener(this);
20 }
21
22
23 public void dealMenuClick(View v) {
24
25 Toast.makeText(this, "这是"+v.getTag(), Toast.LENGTH_SHORT).show();
26
27 }
28
29 }
我们看红色部分的代码,重写了回调方法。无非就是获取之前布局中ImageView中设置的tag,然后把它放在提示框中罢了。运行程序效果如下:
至此,我们的这个案例终结了。主要难点就是自定义ViewGroup,相信你通过这个案例,对自定义ViewGroup应该有了很好的了解了。