289
|
float toXDelta, float fromYDelta, float toYDelta, long startOffset,
|
|
290
|
long duration, Interpolator interpolator) {
|
|
291
|
AnimationSet animationSet = new AnimationSet(false);
|
|
292
|
animationSet.setFillAfter(true);
|
|
293
|
// 收缩过程中,child 逆时针自旋转360度
|
|
294
|
final long preDuration = duration / 2;
|
|
295
|
Animation rotateAnimation = new RotateAnimation(0, 360,
|
|
296
|
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
|
|
297
|
0.5f);
|
|
298
|
rotateAnimation.setStartOffset(startOffset);
|
|
299
|
rotateAnimation.setDuration(preDuration);
|
|
300
|
rotateAnimation.setInterpolator(new LinearInterpolator());
|
|
301
|
rotateAnimation.setFillAfter(true);
|
|
302
|
|
|
303
|
animationSet.addAnimation(rotateAnimation);
|
|
304
|
// 收缩过程中位移,并逆时针旋转360度
|
|
305
|
Animation translateAnimation = new RotateAndTranslateAnimation(0,
|
|
306
|
toXDelta, 0, toYDelta, 360, 720);
|
|
307
|
translateAnimation.setStartOffset(startOffset + preDuration);
|
|
308
|
translateAnimation.setDuration(duration - preDuration);
|
|
309
|
translateAnimation.setInterpolator(interpolator);
|
|
310
|
translateAnimation.setFillAfter(true);
|
|
311
|
|
|
312
|
animationSet.addAnimation(translateAnimation);
|
|
313
|
|
|
314
|
return animationSet;
|
|
315
|
}
|
|
316
|
|
|
317
|
/**
|
|
318
|
* 绑定子菜单项动画
|
|
319
|
*
|
|
320
|
* @param child
|
|
321
|
* @param index
|
|
322
|
* @param duration
|
|
323
|
*/
|
|
324
|
private void bindChildAnimation(final View child, final int index,
|
|
325
|
final long duration) {
|
|
326
|
final boolean expanded = mExpanded;
|
|
327
|
computeCenterXY(position);
|
|
328
|
final int radius = expanded ? 0 : mRadius;
|
|
329
|
|
|
330
|
final int childCount = getChildCount();
|
|
331
|
final float perDegrees;
|
|
332
|
if (mToDegrees - mFromDegrees >= 360) {
|
|
333
|
perDegrees = (mToDegrees - mFromDegrees) / (childCount);
|
|
334
|
} else {
|
|
335
|
perDegrees = (mToDegrees - mFromDegrees) / (childCount - 1);
|
|
336
|
}
|
|
337
|
Rect frame = computeChildFrame(centerX, centerY, radius, mFromDegrees
|
|
338
|
+ index * perDegrees, mChildSize);
|
|
339
|
|
|
340
|
final int toXDelta = frame.left - child.getLeft();// 展开或收缩动画,child沿X轴位移距离
|
|
341
|
final int toYDelta = frame.top - child.getTop();// 展开或收缩动画,child沿Y轴位移距离
|
|
342
|
|
|
343
|
Interpolator interpolator = mExpanded ? new AccelerateInterpolator()
|
|
344
|
: new OvershootInterpolator(1.5f);
|
|
345
|
final long startOffset = computeStartOffset(childCount, mExpanded,
|
|
346
|
index, 0.1f, duration, interpolator);
|
|
347
|
// mExpanded为true,已经展开,收缩动画;为false,展开动画
|
|
348
|
Animation animation = mExpanded ? createShrinkAnimation(0, toXDelta, 0,
|
|
349
|
toYDelta, startOffset, duration, interpolator)
|
|
350
|
: createExpandAnimation(0, toXDelta, 0, toYDelta, startOffset,
|
|
351
|
duration, interpolator);
|
|
352
|
|
|
353
|
final boolean isLast = getTransformedIndex(expanded, childCount, index) == childCount - 1;
|
|
354
|
animation.setAnimationListener(new AnimationListener() {
|
|
355
|
|
|
356
|
@Override
|
|
357
|
public void onAnimationStart(Animation animation) {
|
|
358
|
|
|
359
|
}
|
|
360
|
|
|
361
|
@Override
|
|
362
|
public void onAnimationRepeat(Animation animation) {
|
|
363
|
|
|
364
|
}
|
|
365
|
|
|
366
|
@Override
|
|
367
|
public void onAnimationEnd(Animation animation) {
|
|
368
|
if (isLast) {
|
|
369
|
postDelayed(new Runnable() {
|
|
370
|
|
|
371
|
@Override
|
|
372
|
public void run() {
|
|
373
|
onAllAnimationsEnd();
|
|
374
|
}
|
|
375
|
}, 0);
|
|
376
|
}
|
|
377
|
}
|
|
378
|
});
|
|
379
|
|
|
380
|
child.setAnimation(animation);
|
|
381
|
}
|
|
382
|
|
|
383
|
public boolean isExpanded() {
|
|
384
|
return mExpanded;
|
|
385
|
}
|
|
386
|
|
|
387
|
/**
|
|
388
|
* 设定弧度
|
|
389
|
*/
|
|
390
|
public void setArc(float fromDegrees, float toDegrees, int position) {
|
|
391
|
this.position = position;
|
|
392
|
if (mFromDegrees == fromDegrees && mToDegrees == toDegrees) {
|
|
393
|
return;
|
|
394
|
}
|
|
395
|
|
|
396
|
mFromDegrees = fromDegrees;
|
|
397
|
mToDegrees = toDegrees;
|
|
398
|
computeCenterXY(position);
|
|
399
|
requestLayout();
|
|
400
|
}
|
|
401
|
|
|
402
|
/**
|
|
403
|
* 设定弧度
|
|
404
|
*/
|
|
405
|
public void setArc(float fromDegrees, float toDegrees) {
|
|
406
|
if (mFromDegrees == fromDegrees && mToDegrees == toDegrees) {
|
|
407
|
return;
|
|
408
|
}
|
|
409
|
|
|
410
|
mFromDegrees = fromDegrees;
|
|
411
|
mToDegrees = toDegrees;
|
|
412
|
computeCenterXY(position);
|
|
413
|
requestLayout();
|
|
414
|
}
|
|
415
|
|
|
416
|
|
|
417
|
/**
|
|
418
|
* 设定子菜单项大小
|
|
419
|
*
|
|
420
|
* @param size
|
|
421
|
*/
|
|
422
|
public void setChildSize(int size) {
|
|
423
|
if (mChildSize == size || size < 0) {
|
|
424
|
return;
|
|
425
|
}
|
|
426
|
|
|
427
|
mChildSize = size;
|
|
428
|
|
|
429
|
requestLayout();
|
|
430
|
}
|
|
431
|
|
|
432
|
public int getChildSize() {
|
|
433
|
return mChildSize;
|
|
434
|
}
|
|
435
|
|
|
436
|
public void setPosition(int position) {
|
|
437
|
this.position = position;
|
|
438
|
computeCenterXY(position);
|
|
439
|
invalidate();
|
|
440
|
}
|
|
441
|
/**
|
|
442
|
* 切换中心按钮的展开缩小1
|
|
443
|
* showAnimation一定要设置成true才有动画效果
|
|
444
|
*/
|
|
445
|
public void switchState(final boolean showAnimation, int position) {
|
|
446
|
this.position = position;
|
|
447
|
if (showAnimation) {
|
|
448
|
final int childCount = getChildCount();
|
|
449
|
for (int i = 0; i < childCount; i++) {
|
|
450
|
bindChildAnimation(getChildAt(i), i, 300);
|
|
451
|
}
|
|
452
|
}
|
|
453
|
|
|
454
|
mExpanded = !mExpanded;
|
|
455
|
|
|
456
|
if (!showAnimation) {
|
|
457
|
requestLayout();
|
|
458
|
}
|
|
459
|
|
|
460
|
invalidate();
|
|
461
|
}
|
|
462
|
/**
|
|
463
|
* 切换中心按钮的展开缩小
|
|
464
|
*
|
|
465
|
* @param showAnimation
|
|
466
|
*/
|
|
467
|
public void switchState(final boolean showAnimation) {
|
|
468
|
if (showAnimation) {
|
|
469
|
final int childCount = getChildCount();
|
|
470
|
for (int i = 0; i < childCount; i++) {
|
|
471
|
bindChildAnimation(getChildAt(i), i, 300);
|
|
472
|
}
|
|
473
|
}
|
|
474
|
|
|
475
|
mExpanded = !mExpanded;
|
|
476
|
|
|
477
|
if (!showAnimation) {
|
|
478
|
requestLayout();
|
|
479
|
}
|
|
480
|
|
|
481
|
invalidate();
|
|
482
|
}
|
|
483
|
|
|
484
|
/**
|
|
485
|
* 结束所有动画
|
|
486
|
*/
|
|
487
|
private void onAllAnimationsEnd() {
|
|
488
|
final int childCount = getChildCount();
|
|
489
|
for (int i = 0; i < childCount; i++) {
|
|
490
|
getChildAt(i).clearAnimation();
|
|
491
|
}
|
|
492
|
|
|
493
|
requestLayout();
|
|
494
|
}
|
|
495
|
}
|
|
@ -0,0 +1,94 @@
|
|
1
|
package com.ai.ipu.ipu_pathmenu;
|
|
2
|
|
|
3
|
|
|
4
|
import android.app.Service;
|
|
5
|
import android.content.Intent;
|
|
6
|
import android.os.IBinder;
|
|
7
|
import android.view.View;
|
|
8
|
import android.view.View.OnClickListener;
|
|
9
|
import android.widget.ImageView;
|
|
10
|
import android.widget.Toast;
|
|
11
|
|
|
12
|
public class PathMenuService extends Service {
|
|
13
|
PathMenu pathMenu;
|
|
14
|
private static int[] ITEM_DRAWABLES = {};
|
|
15
|
|
|
16
|
@Override
|
|
17
|
public IBinder onBind(Intent intent) {
|
|
18
|
return null;
|
|
19
|
}
|
|
20
|
|
|
21
|
@Override
|
|
22
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
23
|
ITEM_DRAWABLES = intent.getIntArrayExtra("icons");
|
|
24
|
pathMenu = new PathMenu(getApplicationContext());
|
|
25
|
initPathMenu(pathMenu, ITEM_DRAWABLES);// 初始化子菜单
|
|
26
|
return super.onStartCommand(intent, flags, startId);
|
|
27
|
}
|
|
28
|
|
|
29
|
@Override
|
|
30
|
public void onDestroy() {
|
|
31
|
super.onDestroy();
|
|
32
|
if (pathMenu != null) {
|
|
33
|
pathMenu.hidePathMenu();
|
|
34
|
}
|
|
35
|
}
|
|
36
|
|
|
37
|
/**
|
|
38
|
* 初始化子菜单图片、点击事件
|
|
39
|
*
|
|
40
|
* @param menu
|
|
41
|
* @param itemDrawables
|
|
42
|
*/
|
|
43
|
private void initPathMenu(final PathMenu menu, final int[] itemDrawables) {
|
|
44
|
final int itemCount = itemDrawables.length;
|
|
45
|
for (int i = 0; i < itemCount; i++) {
|
|
46
|
ImageView item = new ImageView(this);
|
|
47
|
item.setImageResource(itemDrawables[i]);
|
|
48
|
final int index = i;
|
|
49
|
menu.addItem(item, new OnClickListener() {
|
|
50
|
|
|
51
|
@Override
|
|
52
|
public void onClick(View v) {
|
|
53
|
switch (index) {
|
|
54
|
case 0:
|
|
55
|
Toast.makeText(PathMenuService.this, "关闭菜单",
|
|
56
|
Toast.LENGTH_SHORT).show();
|
|
57
|
stopSelf();// 关闭菜单
|
|
58
|
break;
|
|
59
|
|
|
60
|
case 1:
|
|
61
|
Toast.makeText(PathMenuService.this, "第1个被点击",
|
|
62
|
Toast.LENGTH_SHORT).show();
|
|
63
|
break;
|
|
64
|
|
|
65
|
case 2:
|
|
66
|
Toast.makeText(PathMenuService.this, "第2个被点击",
|
|
67
|
Toast.LENGTH_SHORT).show();
|
|
68
|
break;
|
|
69
|
|
|
70
|
case 3:
|
|
71
|
Toast.makeText(PathMenuService.this, "第3个被点击",
|
|
72
|
Toast.LENGTH_SHORT).show();
|
|
73
|
break;
|
|
74
|
case 4:
|
|
75
|
Toast.makeText(PathMenuService.this, "第4个被点击",
|
|
76
|
Toast.LENGTH_SHORT).show();
|
|
77
|
break;
|
|
78
|
case 5:
|
|
79
|
Toast.makeText(PathMenuService.this, "第5个被点击",
|
|
80
|
Toast.LENGTH_SHORT).show();
|
|
81
|
break;
|
|
82
|
case 6:
|
|
83
|
Toast.makeText(PathMenuService.this, "第6个被点击",
|
|
84
|
Toast.LENGTH_SHORT).show();
|
|
85
|
break;
|
|
86
|
|
|
87
|
|
|
88
|
}
|
|
89
|
}
|
|
90
|
});
|
|
91
|
}
|
|
92
|
}
|
|
93
|
|
|
94
|
}
|
|
@ -0,0 +1,123 @@
|
|
1
|
package com.ai.ipu.ipu_pathmenu;
|
|
2
|
|
|
3
|
import android.view.animation.Animation;
|
|
4
|
import android.view.animation.Transformation;
|
|
5
|
/**
|
|
6
|
* 自定义动画类
|
|
7
|
* 控制对象的位置,以对象的中心旋转
|
|
8
|
* @author 何凌波
|
|
9
|
*
|
|
10
|
*/
|
|
11
|
public class RotateAndTranslateAnimation extends Animation {
|
|
12
|
private int mFromXType = ABSOLUTE;
|
|
13
|
|
|
14
|
private int mToXType = ABSOLUTE;
|
|
15
|
|
|
16
|
private int mFromYType = ABSOLUTE;
|
|
17
|
|
|
18
|
private int mToYType = ABSOLUTE;
|
|
19
|
|
|
20
|
private float mFromXValue = 0.0f;
|
|
21
|
|
|
22
|
private float mToXValue = 0.0f;
|
|
23
|
|
|
24
|
private float mFromYValue = 0.0f;
|
|
25
|
|
|
26
|
private float mToYValue = 0.0f;
|
|
27
|
|
|
28
|
private float mFromXDelta;
|
|
29
|
|
|
30
|
private float mToXDelta;
|
|
31
|
|
|
32
|
private float mFromYDelta;
|
|
33
|
|
|
34
|
private float mToYDelta;
|
|
35
|
|
|
36
|
private float mFromDegrees;
|
|
37
|
|
|
38
|
private float mToDegrees;
|
|
39
|
|
|
40
|
private int mPivotXType = ABSOLUTE;
|
|
41
|
|
|
42
|
private int mPivotYType = ABSOLUTE;
|
|
43
|
|
|
44
|
private float mPivotXValue = 0.0f;
|
|
45
|
|
|
46
|
private float mPivotYValue = 0.0f;
|
|
47
|
|
|
48
|
private float mPivotX;
|
|
49
|
|
|
50
|
private float mPivotY;
|
|
51
|
|
|
52
|
/**
|
|
53
|
* 位移动画的构造函数
|
|
54
|
* @param fromXDelta
|
|
55
|
* 动画开始时的X坐标
|
|
56
|
* @param toXDelta
|
|
57
|
* 动画结束时的X坐标
|
|
58
|
* @param fromYDelta
|
|
59
|
* 动画开始时的Y坐标
|
|
60
|
* @param toYDelta
|
|
61
|
* 动画结束时的Y坐标
|
|
62
|
*
|
|
63
|
* @param fromDegrees
|
|
64
|
* 旋转开始时的角度
|
|
65
|
* @param toDegrees
|
|
66
|
* 旋转结束时的角度
|
|
67
|
*/
|
|
68
|
public RotateAndTranslateAnimation(float fromXDelta, float toXDelta,
|
|
69
|
float fromYDelta, float toYDelta, float fromDegrees, float toDegrees) {
|
|
70
|
mFromXValue = fromXDelta;
|
|
71
|
mToXValue = toXDelta;
|
|
72
|
mFromYValue = fromYDelta;
|
|
73
|
mToYValue = toYDelta;
|
|
74
|
|
|
75
|
mFromXType = ABSOLUTE;
|
|
76
|
mToXType = ABSOLUTE;
|
|
77
|
mFromYType = ABSOLUTE;
|
|
78
|
mToYType = ABSOLUTE;
|
|
79
|
|
|
80
|
mFromDegrees = fromDegrees;
|
|
81
|
mToDegrees = toDegrees;
|
|
82
|
|
|
83
|
mPivotXValue = 0.5f;
|
|
84
|
mPivotXType = RELATIVE_TO_SELF;
|
|
85
|
mPivotYValue = 0.5f;
|
|
86
|
mPivotYType = RELATIVE_TO_SELF;
|
|
87
|
}
|
|
88
|
|
|
89
|
@Override
|
|
90
|
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
|
91
|
final float degrees = mFromDegrees
|
|
92
|
+ ((mToDegrees - mFromDegrees) * interpolatedTime);
|
|
93
|
if (mPivotX == 0.0f && mPivotY == 0.0f) {
|
|
94
|
t.getMatrix().setRotate(degrees);
|
|
95
|
} else {
|
|
96
|
t.getMatrix().setRotate(degrees, mPivotX, mPivotY);
|
|
97
|
}
|
|
98
|
|
|
99
|
float dx = mFromXDelta;
|
|
100
|
float dy = mFromYDelta;
|
|
101
|
if (mFromXDelta != mToXDelta) {
|
|
102
|
dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
|
|
103
|
}
|
|
104
|
if (mFromYDelta != mToYDelta) {
|
|
105
|
dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
|
|
106
|
}
|
|
107
|
|
|
108
|
t.getMatrix().postTranslate(dx, dy);
|
|
109
|
}
|
|
110
|
|
|
111
|
@Override
|
|
112
|
public void initialize(int width, int height, int parentWidth,
|
|
113
|
int parentHeight) {
|
|
114
|
super.initialize(width, height, parentWidth, parentHeight);
|
|
115
|
mFromXDelta = resolveSize(mFromXType, mFromXValue, width, parentWidth);
|
|
116
|
mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth);
|
|
117
|
mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight);
|
|
118
|
mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight);
|
|
119
|
|
|
120
|
mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);
|
|
121
|
mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);
|
|
122
|
}
|
|
123
|
}
|