|
@ -3,6 +3,8 @@ package com.wade.mobile.ui.comp.menu;
|
3
|
3
|
import java.util.HashMap;
|
4
|
4
|
import java.util.Map;
|
5
|
5
|
|
|
6
|
import com.wade.mobile.ui.anim.AnimationTool;
|
|
7
|
|
6
|
8
|
import android.R.anim;
|
7
|
9
|
import android.content.Context;
|
8
|
10
|
import android.content.res.Resources;
|
|
@ -16,20 +18,19 @@ import android.view.animation.Animation.AnimationListener;
|
16
|
18
|
import android.widget.Button;
|
17
|
19
|
import android.widget.RelativeLayout;
|
18
|
20
|
|
19
|
|
import com.wade.mobile.ui.anim.AnimationTool;
|
20
|
|
|
21
|
21
|
public class PathMenu extends RelativeLayout{
|
22
|
22
|
private Window window;
|
23
|
23
|
private int leftMargin, bottomMargin; //左边距和底边距
|
24
|
24
|
private int contentTopHeight; //屏幕状态栏宽高
|
25
|
|
private int buttonRadius; //图片按钮半径
|
|
25
|
private int buttonDiameter; //图片按钮直径
|
26
|
26
|
private int radius; //扇形菜单半径
|
|
27
|
private int originalRadius; //原始菜单半径
|
27
|
28
|
private int timeSpent = 300; //最长动画耗时
|
28
|
29
|
private int alpha = 127; //透明度 0-255
|
29
|
30
|
private double circle = Math.PI; //扇形菜单占据一圈的比例,0~1
|
30
|
|
/*
|
31
|
|
private int intervalTimeSpent; //菜单按钮之间的时间间隔
|
32
|
|
*/
|
|
31
|
|
|
32
|
//private int intervalTimeSpent; //菜单按钮之间的时间间隔
|
|
33
|
|
33
|
34
|
private Button[] buttons; //Path菜单按钮引用
|
34
|
35
|
private int[] drawables; //菜单子按钮的图片
|
35
|
36
|
private int menuDrawable; //菜单按钮的图片
|
|
@ -46,6 +47,13 @@ public class PathMenu extends RelativeLayout{
|
46
|
47
|
|
47
|
48
|
private Map<String,Animation> animCache; //动画缓存
|
48
|
49
|
|
|
50
|
private Position position; //pathmenu位置
|
|
51
|
private boolean isLeft,isRight,isUp,isBottom; //是否停边
|
|
52
|
|
|
53
|
int screenWidth,screenHeight;
|
|
54
|
|
|
55
|
private long touchStartTime;
|
|
56
|
|
49
|
57
|
/**click事件*/
|
50
|
58
|
View.OnClickListener clickListener=new View.OnClickListener(){
|
51
|
59
|
public void onClick(View v) {
|
|
@ -72,31 +80,48 @@ public class PathMenu extends RelativeLayout{
|
72
|
80
|
int btnHeight = menuBtn.getHeight();
|
73
|
81
|
switch (event.getAction()) {
|
74
|
82
|
case MotionEvent.ACTION_DOWN:
|
|
83
|
touchStartTime = System.currentTimeMillis();
|
75
|
84
|
focusView(menuBtn);//获取焦点
|
76
|
85
|
menuBtnX = menuBtn.getLeft();
|
77
|
86
|
menuBtnY = menuBtn.getBottom();
|
78
|
87
|
break;
|
79
|
88
|
case MotionEvent.ACTION_MOVE:
|
80
|
|
if(isCanDrag){
|
81
|
|
int x_move = (int) event.getRawX();
|
82
|
|
int y_move = (int) event.getRawY();
|
83
|
|
|
84
|
|
/*滑动到一定程度时候才出发拖拽*/
|
85
|
|
if(!isDrag&&((Math.abs(menuBtnX+btnWidth/2-x_move)>buttonRadius)
|
86
|
|
||(Math.abs(menuBtnY+contentTopHeight-btnHeight/2-y_move)>buttonRadius))){
|
87
|
|
isDrag = true;
|
88
|
|
}
|
89
|
|
|
90
|
|
if(isDrag){
|
91
|
|
int top = y_move - btnHeight / 2 - contentTopHeight;
|
92
|
|
int bottom = y_move + btnHeight / 2 - contentTopHeight;
|
93
|
|
/*防止越界top,标题栏+状态栏*/
|
94
|
|
if(y_move<contentTopHeight){
|
95
|
|
top = -btnHeight / 2;
|
96
|
|
bottom = btnHeight / 2;
|
|
89
|
if(500 <= System.currentTimeMillis() - touchStartTime){ //当执行触摸时间大于0.5秒时,才执行移动;避免因屏幕敏感导致点击变成滑动事件
|
|
90
|
if(isCanDrag){
|
|
91
|
int x_move = (int) event.getRawX();
|
|
92
|
int y_move = (int) event.getRawY();
|
|
93
|
/*滑动到一定程度时候才出发拖拽*/
|
|
94
|
if(!isDrag&&((Math.abs(menuBtnX+btnWidth/2-x_move)>(buttonDiameter/8))
|
|
95
|
||(Math.abs(menuBtnY+contentTopHeight-btnHeight/2-y_move)>(buttonDiameter/8)))){
|
|
96
|
isDrag = true;
|
|
97
|
}
|
|
98
|
|
|
99
|
if(isDrag){
|
|
100
|
int top = y_move - btnHeight / 2 - contentTopHeight;
|
|
101
|
int bottom = y_move + btnHeight / 2 - contentTopHeight;
|
|
102
|
int left = x_move - btnWidth / 2;
|
|
103
|
int right = x_move + btnWidth / 2;
|
|
104
|
/*防止越界top,标题栏+状态栏*/
|
|
105
|
if(y_move<contentTopHeight + btnHeight/2){
|
|
106
|
top = 0;
|
|
107
|
bottom = btnHeight;
|
|
108
|
}
|
|
109
|
/*防止越界bottom*/
|
|
110
|
if(screenHeight - y_move - btnHeight/2<= 0){
|
|
111
|
top = screenHeight - contentTopHeight - btnHeight;
|
|
112
|
bottom = screenHeight - contentTopHeight ;
|
|
113
|
}
|
|
114
|
if(x_move <= btnWidth/2){
|
|
115
|
left = 0;
|
|
116
|
right = btnWidth;
|
|
117
|
}
|
|
118
|
if(x_move >= screenWidth - btnWidth/2){
|
|
119
|
left = screenWidth - btnWidth;
|
|
120
|
right = screenWidth;
|
|
121
|
}
|
|
122
|
menuBtn.layout(left , top, right, bottom);
|
|
123
|
menuBtn.postInvalidate();
|
97
|
124
|
}
|
98
|
|
menuBtn.layout(x_move - btnWidth / 2, top, x_move + btnWidth / 2, bottom);
|
99
|
|
menuBtn.postInvalidate();
|
100
|
125
|
}
|
101
|
126
|
}
|
102
|
127
|
break;
|
|
@ -119,9 +144,35 @@ public class PathMenu extends RelativeLayout{
|
119
|
144
|
isDrag = false;
|
120
|
145
|
}else{
|
121
|
146
|
if(isOpen()){
|
122
|
|
PathMenu.this.shutMenu();
|
|
147
|
PathMenu.this.shutMenu(position);
|
123
|
148
|
}else{
|
124
|
|
PathMenu.this.openMenu();
|
|
149
|
//判断靠边类型
|
|
150
|
int x_up = menuBtn.getLeft();
|
|
151
|
int y_up = menuBtn.getBottom();
|
|
152
|
|
|
153
|
/*根据离屏幕边框的距离判断菜单展开形状*/
|
|
154
|
if (x_up + buttonDiameter/2 < originalRadius) {//x=0是指圆形按钮的最左边,而否中心
|
|
155
|
isLeft = true;
|
|
156
|
} else {
|
|
157
|
isLeft = false;
|
|
158
|
}
|
|
159
|
if (screenWidth - x_up - buttonDiameter/2 < originalRadius) {
|
|
160
|
isRight = true;
|
|
161
|
} else {
|
|
162
|
isRight = false;
|
|
163
|
}
|
|
164
|
if (y_up - buttonDiameter/2 - contentTopHeight< originalRadius) {
|
|
165
|
isUp = true;
|
|
166
|
} else {
|
|
167
|
isUp = false;
|
|
168
|
}
|
|
169
|
if (screenHeight - y_up - buttonDiameter/2 < originalRadius) {
|
|
170
|
isBottom = true;
|
|
171
|
} else {
|
|
172
|
isBottom = false;
|
|
173
|
}
|
|
174
|
position = getPosition(isLeft, isRight, isUp, isBottom);
|
|
175
|
PathMenu.this.openMenu(position);
|
125
|
176
|
}
|
126
|
177
|
}
|
127
|
178
|
break;
|
|
@ -148,9 +199,9 @@ public class PathMenu extends RelativeLayout{
|
148
|
199
|
}
|
149
|
200
|
|
150
|
201
|
/*********************设置属性 start*************************/
|
151
|
|
public PathMenu setButtonRadius(int buttonRadius){
|
152
|
|
if(this.buttonRadius<=0){
|
153
|
|
this.buttonRadius = buttonRadius;
|
|
202
|
public PathMenu setButtonDiameter(int buttonDiameter){
|
|
203
|
if(this.buttonDiameter<=0){
|
|
204
|
this.buttonDiameter = buttonDiameter;
|
154
|
205
|
}
|
155
|
206
|
return this;
|
156
|
207
|
}
|
|
@ -158,6 +209,7 @@ public class PathMenu extends RelativeLayout{
|
158
|
209
|
public PathMenu setRadius(int radius){
|
159
|
210
|
if(this.radius<=0){
|
160
|
211
|
this.radius = radius;
|
|
212
|
this.originalRadius = radius;
|
161
|
213
|
}
|
162
|
214
|
return this;
|
163
|
215
|
}
|
|
@ -178,7 +230,11 @@ public class PathMenu extends RelativeLayout{
|
178
|
230
|
if(circle>1)
|
179
|
231
|
circle = 1;
|
180
|
232
|
this.circle = 2*Math.PI*circle;
|
181
|
|
this.angle=(float)this.circle/(buttons.length-1);
|
|
233
|
if(circle == 1){
|
|
234
|
this.angle=(float)this.circle/(buttons.length);
|
|
235
|
}else {
|
|
236
|
this.angle=(float)this.circle/(buttons.length - 1);
|
|
237
|
}
|
182
|
238
|
return this;
|
183
|
239
|
}
|
184
|
240
|
|
|
@ -210,7 +266,7 @@ public class PathMenu extends RelativeLayout{
|
210
|
266
|
menuBtn.setBackgroundDrawable(resources.getDrawable(menuDrawable));
|
211
|
267
|
this.addView(menuBtn);
|
212
|
268
|
|
213
|
|
angle=(float)circle/(buttons.length-1);
|
|
269
|
// angle=(float)circle/(buttons.length-1);
|
214
|
270
|
|
215
|
271
|
setVisibility(View.INVISIBLE);
|
216
|
272
|
}
|
|
@ -225,18 +281,19 @@ public class PathMenu extends RelativeLayout{
|
225
|
281
|
/*初始化默认值*/
|
226
|
282
|
DisplayMetrics dm = new DisplayMetrics();
|
227
|
283
|
window.getWindowManager().getDefaultDisplay().getMetrics(dm); //取得窗口属性
|
228
|
|
int screenWidth = (int)dm.widthPixels; //窗口的宽度
|
|
284
|
screenWidth = (int)dm.widthPixels; //窗口的宽度
|
|
285
|
screenHeight = (int)dm.heightPixels;
|
229
|
286
|
|
230
|
287
|
/*计算标题栏和状态栏的高度*/
|
231
|
288
|
if(this.contentTopHeight<=0){
|
232
|
289
|
this.contentTopHeight = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
|
233
|
290
|
}
|
234
|
291
|
|
235
|
|
setButtonRadius(screenWidth / 8);
|
236
|
|
setRadius(this.buttonRadius * 2);
|
|
292
|
setButtonDiameter(screenWidth / 8);
|
|
293
|
setRadius(this.buttonDiameter * 2);
|
237
|
294
|
|
238
|
|
leftMargin = xposition - buttonRadius/2;
|
239
|
|
bottomMargin = yposition - buttonRadius - contentTopHeight;
|
|
295
|
leftMargin = xposition - buttonDiameter/2;
|
|
296
|
bottomMargin = yposition - buttonDiameter - contentTopHeight;
|
240
|
297
|
|
241
|
298
|
/*触发点越界时候的情况*/
|
242
|
299
|
/*if(leftMargin + radius + buttonWidth > spacingWidth){
|
|
@ -249,8 +306,8 @@ public class PathMenu extends RelativeLayout{
|
249
|
306
|
/*设置所有按钮的位置*/
|
250
|
307
|
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
251
|
308
|
ViewGroup.LayoutParams.WRAP_CONTENT);
|
252
|
|
params.height = buttonRadius;
|
253
|
|
params.width = buttonRadius;
|
|
309
|
params.height = buttonDiameter;
|
|
310
|
params.width = buttonDiameter;
|
254
|
311
|
params.setMargins(leftMargin, bottomMargin, 0, 0);
|
255
|
312
|
|
256
|
313
|
for (int i = 0; i < buttons.length; i++) {
|
|
@ -288,13 +345,19 @@ public class PathMenu extends RelativeLayout{
|
288
|
345
|
isOpen = true;
|
289
|
346
|
}
|
290
|
347
|
|
291
|
|
public void openMenu() {
|
|
348
|
public void openMenu(Position position) {
|
|
349
|
// if(position != 0 && position % 2 == 0){//当position为扇形,打开时初始半径变大1.5倍;
|
|
350
|
// radius *= 1.5;
|
|
351
|
// }
|
|
352
|
|
292
|
353
|
int toX,toY;
|
293
|
354
|
for(int i=0;i<buttons.length;i++){
|
294
|
355
|
buttons[i].setVisibility(View.VISIBLE);
|
295
|
356
|
|
296
|
|
toX=(int)(radius*Math.sin(i*angle-Math.PI/2));
|
297
|
|
toY=(int)(radius*Math.cos(i*angle-Math.PI/2));
|
|
357
|
// toX=(int)(radius*Math.sin(i*angle));
|
|
358
|
// toY=(int)(radius*Math.cos(i*angle));
|
|
359
|
toX = computOffset(position, i)[0];
|
|
360
|
toY = computOffset(position, i)[1];
|
298
|
361
|
|
299
|
362
|
Animation animation = animOpen(toX, -toY, timeSpent/*+i*intervalTimeSpent*/);
|
300
|
363
|
/*animation.setFillAfter(true);*/ //达不到既定的效果
|
|
@ -309,15 +372,29 @@ public class PathMenu extends RelativeLayout{
|
309
|
372
|
/**
|
310
|
373
|
* 关闭菜单
|
311
|
374
|
*/
|
312
|
|
public void shutMenu(){
|
|
375
|
/**
|
|
376
|
* @param position
|
|
377
|
*/
|
|
378
|
/**
|
|
379
|
* @param position
|
|
380
|
*/
|
|
381
|
public void shutMenu(Position position){
|
313
|
382
|
float toX, toY;
|
314
|
383
|
for (int i = 0; i < buttons.length; i++) {
|
315
|
|
toX=(float)(radius*Math.sin(i*angle-Math.PI/2));
|
316
|
|
toY=(float)(radius*Math.cos(i*angle-Math.PI/2));
|
|
384
|
// toX=(float)(radius*Math.sin(i*angle - Math.PI));
|
|
385
|
// toY=(float)(radius*Math.cos(i*angle - Math.PI));
|
|
386
|
|
|
387
|
toX = computOffset(position, i)[0];
|
|
388
|
toY = computOffset(position, i)[1];
|
|
389
|
|
317
|
390
|
buttons[i].startAnimation(animShut(buttons[i], -toX, toY, timeSpent));//点击按钮变大
|
318
|
391
|
}
|
319
|
392
|
isOpen = false;
|
320
|
393
|
isCanDrag = true;
|
|
394
|
|
|
395
|
// if(position != 0 && position % 2 == 0){//当position为扇形,关闭时恢复初始半径;
|
|
396
|
// radius /= 1.5;
|
|
397
|
// }
|
321
|
398
|
}
|
322
|
399
|
|
323
|
400
|
/************************动画效果实现start***************************/
|
|
@ -363,7 +440,7 @@ public class PathMenu extends RelativeLayout{
|
363
|
440
|
public void reset(){
|
364
|
441
|
/*解决Home出去时的BUG*/
|
365
|
442
|
if(this.isOpen){
|
366
|
|
shutMenu();
|
|
443
|
shutMenu(position);
|
367
|
444
|
}
|
368
|
445
|
this.isOpen = false;
|
369
|
446
|
this.isCanDrag = true;
|
|
@ -402,7 +479,7 @@ public class PathMenu extends RelativeLayout{
|
402
|
479
|
button.clearAnimation();
|
403
|
480
|
|
404
|
481
|
/*1.设置button,解决跨线程调用,Handle的实现在ViewRoot中,触发invalidate()*/
|
405
|
|
button.layout(left, bottom,left+buttonRadius, bottom+buttonRadius);
|
|
482
|
button.layout(left, bottom,left+buttonDiameter, bottom+buttonDiameter);
|
406
|
483
|
button.postInvalidate();
|
407
|
484
|
/*2.最原始的方式设置button*/
|
408
|
485
|
/*RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
@ -428,4 +505,128 @@ public class PathMenu extends RelativeLayout{
|
428
|
505
|
public void setOnButtonClickListener(OnButtonClickListener onButtonClickListener){
|
429
|
506
|
this.onButtonClickListener = onButtonClickListener;
|
430
|
507
|
}
|
431
|
|
}
|
|
508
|
|
|
509
|
/********展开形状判定**********/
|
|
510
|
public static enum Position {
|
|
511
|
CENTER(0), //中心正圆
|
|
512
|
TOP_CENTER(1), //上中半圆
|
|
513
|
TOP_RIGHT(2), //上右扇形
|
|
514
|
RIGHT_CENTER(3), //右中半圆
|
|
515
|
BOTTOM_RIGHT(4), //底右扇形
|
|
516
|
BOTTOM_CENTER(5), //底中半圆
|
|
517
|
BOTTOM_LEFT(6), //底左扇形
|
|
518
|
LEFT_CENTER(7), //左中半圆
|
|
519
|
TOP_LEFT(8); //上左扇形
|
|
520
|
|
|
521
|
private int position;
|
|
522
|
|
|
523
|
private Position(int position) {
|
|
524
|
this.position = position;
|
|
525
|
}
|
|
526
|
|
|
527
|
public int getPosition() {
|
|
528
|
return this.position;
|
|
529
|
}
|
|
530
|
}
|
|
531
|
|
|
532
|
/**
|
|
533
|
* 通过距离动态判断停靠位置
|
|
534
|
* @param isLeft
|
|
535
|
* @param isRight
|
|
536
|
* @param isUp
|
|
537
|
* @param isBottom
|
|
538
|
* @return
|
|
539
|
*/
|
|
540
|
private Position getPosition(boolean isLeft,boolean isRight,boolean isUp,boolean isBottom){
|
|
541
|
if (isBottom) {
|
|
542
|
if(isRight){
|
|
543
|
return Position.BOTTOM_RIGHT;
|
|
544
|
}else if (isLeft) {
|
|
545
|
return Position.BOTTOM_LEFT;
|
|
546
|
}else{
|
|
547
|
return Position.BOTTOM_CENTER;
|
|
548
|
}
|
|
549
|
}else if (isUp) {
|
|
550
|
if(isRight){
|
|
551
|
return Position.TOP_RIGHT;
|
|
552
|
}else if (isLeft) {
|
|
553
|
return Position.TOP_LEFT;
|
|
554
|
}else{
|
|
555
|
return Position.TOP_CENTER;
|
|
556
|
}
|
|
557
|
}else if (isRight) {
|
|
558
|
return Position.RIGHT_CENTER;
|
|
559
|
}else if (isLeft) {
|
|
560
|
return Position.LEFT_CENTER;
|
|
561
|
}else{
|
|
562
|
return Position.CENTER;
|
|
563
|
}
|
|
564
|
}
|
|
565
|
/**
|
|
566
|
* 偏移量计算
|
|
567
|
*/
|
|
568
|
private int[] computOffset(Position position,int i){
|
|
569
|
int toX = 0,toY =0 ;
|
|
570
|
radius = buttonDiameter * 2;
|
|
571
|
if(position.getPosition() != 0 && position.getPosition() % 2 == 0){//当position为扇形,打开时初始半径变大1.5倍;
|
|
572
|
radius *= 1.5;
|
|
573
|
}
|
|
574
|
switch (position) {
|
|
575
|
case CENTER:
|
|
576
|
setCircle(1);
|
|
577
|
toX =(int)(radius*Math.sin(i*angle));
|
|
578
|
toY =(int)(radius*Math.cos(i*angle));
|
|
579
|
break;
|
|
580
|
case TOP_CENTER:
|
|
581
|
setCircle(0.5f);
|
|
582
|
toX = (int)(radius*Math.sin(i*angle + Math.PI/2));
|
|
583
|
toY = (int)(radius*Math.cos(i*angle + Math.PI/2));
|
|
584
|
break;
|
|
585
|
case TOP_RIGHT:
|
|
586
|
setCircle(0.25f);
|
|
587
|
toX = (int)(radius*Math.sin(i*angle + Math.PI));
|
|
588
|
toY = (int)(radius*Math.cos(i*angle + Math.PI));
|
|
589
|
break;
|
|
590
|
case RIGHT_CENTER:
|
|
591
|
setCircle(0.5f);
|
|
592
|
toX = (int)(radius*Math.sin(i*angle + Math.PI));
|
|
593
|
toY = (int)(radius*Math.cos(i*angle + Math.PI));
|
|
594
|
break;
|
|
595
|
case BOTTOM_RIGHT:
|
|
596
|
setCircle(0.25f);
|
|
597
|
toX = (int)(radius*Math.sin(i*angle - Math.PI/2));
|
|
598
|
toY = (int)(radius*Math.cos(i*angle - Math.PI/2));
|
|
599
|
break;
|
|
600
|
case BOTTOM_CENTER:
|
|
601
|
setCircle(0.5f);
|
|
602
|
toX = (int)(radius*Math.sin(i*angle - Math.PI/2));
|
|
603
|
toY = (int)(radius*Math.cos(i*angle - Math.PI/2));
|
|
604
|
break;
|
|
605
|
case BOTTOM_LEFT:
|
|
606
|
setCircle(0.25f);
|
|
607
|
toX = (int)(radius*Math.sin(i*angle));
|
|
608
|
toY = (int)(radius*Math.cos(i*angle));
|
|
609
|
break;
|
|
610
|
case LEFT_CENTER:
|
|
611
|
setCircle(0.5f);
|
|
612
|
toX = (int)(radius*Math.sin(i*angle));
|
|
613
|
toY = (int)(radius*Math.cos(i*angle));
|
|
614
|
break;
|
|
615
|
case TOP_LEFT:
|
|
616
|
setCircle(0.25f);
|
|
617
|
toX = (int)(radius*Math.sin(i*angle + Math.PI/2));
|
|
618
|
toY = (int)(radius*Math.cos(i*angle + Math.PI/2));
|
|
619
|
break;
|
|
620
|
default:
|
|
621
|
break;
|
|
622
|
}
|
|
623
|
int [] xy = {toX,toY};
|
|
624
|
return xy;
|
|
625
|
}
|
|
626
|
|
|
627
|
public PathMenu setPosition(Position position){
|
|
628
|
this.position = position;
|
|
629
|
return this;
|
|
630
|
}
|
|
631
|
|
|
632
|
}
|