class="add-code nl-9 ol-9">
|
9
|
import android.util.Log;
|
|
10
|
import android.view.Gravity;
|
|
11
|
import android.view.LayoutInflater;
|
|
12
|
import android.view.MotionEvent;
|
|
13
|
import android.view.View;
|
|
14
|
import android.view.View.OnTouchListener;
|
|
15
|
import android.view.ViewTreeObserver;
|
|
16
|
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
|
17
|
import android.view.WindowManager;
|
|
18
|
import android.view.animation.AlphaAnimation;
|
|
19
|
import android.view.animation.Animation;
|
|
20
|
import android.view.animation.Animation.AnimationListener;
|
|
21
|
import android.view.animation.AnimationSet;
|
|
22
|
import android.view.animation.DecelerateInterpolator;
|
|
23
|
import android.view.animation.RotateAnimation;
|
|
24
|
import android.view.animation.ScaleAnimation;
|
|
25
|
import android.widget.FrameLayout;
|
|
26
|
import android.widget.ImageView;
|
|
27
|
|
|
28
|
/**
|
|
29
|
* 自定义菜单
|
|
30
|
*
|
|
31
|
* @author 何凌波
|
|
32
|
*
|
|
33
|
*/
|
|
34
|
public class PathMenu extends FrameLayout implements OnTouchListener {
|
|
35
|
private PathMenuLayout mPathMenuLayout;
|
|
36
|
private FrameLayout controlLayout;
|
|
37
|
private ImageView mHintView;// 中心按钮显示图片
|
|
38
|
private WindowManager mWindowManager;// 当前view的窗口管理器
|
|
39
|
// WindowMananger的params,控制这个值可以将自定义的view设置到窗口管理器中
|
|
40
|
private WindowManager.LayoutParams mWmParams;
|
|
41
|
private float mTouchStartX;// 记录首次按下的位置x
|
|
42
|
private float mTouchStartY;// 记录首次按下的位置y
|
|
43
|
|
|
44
|
private int mScreenWidth;// 屏幕宽度
|
|
45
|
private int mScreenHeight;// 屏幕高度
|
|
46
|
private boolean mDraging;// 是否拖动中
|
|
47
|
private Context mContext;
|
|
48
|
|
|
49
|
private int position;// 按钮的位置
|
|
50
|
public static final int LEFT_TOP = 1;
|
|
51
|
public static final int CENTER_TOP = 2;
|
|
52
|
public static final int RIGHT_TOP = 3;
|
|
53
|
public static final int LEFT_CENTER = 4;
|
|
54
|
public static final int CENTER = 5;
|
|
55
|
public static final int RIGHT_CENTER = 6;
|
|
56
|
public static final int LEFT_BOTTOM = 7;
|
|
57
|
public static final int CENTER_BOTTOM = 8;
|
|
58
|
public static final int RIGHT_BOTTOM = 9;
|
|
59
|
|
|
60
|
public PathMenu(Context context) {
|
|
61
|
super(context);
|
|
62
|
this.mContext = context;
|
|
63
|
init(mContext);
|
|
64
|
}
|
|
65
|
|
|
66
|
public PathMenu(Context context, AttributeSet attrs) {
|
|
67
|
super(context, attrs);
|
|
68
|
this.mContext = context;
|
|
69
|
init(mContext);
|
|
70
|
applyAttrs(attrs);
|
|
71
|
}
|
|
72
|
|
|
73
|
/**
|
|
74
|
* 初始化中心按钮布局,加载布局文件,设置触摸事件
|
|
75
|
*
|
|
76
|
* @param context
|
|
77
|
*/
|
|
78
|
private void init(Context context) {
|
|
79
|
LayoutInflater li = (LayoutInflater) context
|
|
80
|
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
81
|
li.inflate(R.layout.float_menu, this);
|
|
82
|
mWindowManager = (WindowManager) context
|
|
83
|
.getSystemService(Context.WINDOW_SERVICE);// 获取系统的窗口服务
|
|
84
|
DisplayMetrics dm = new DisplayMetrics();
|
|
85
|
mWindowManager.getDefaultDisplay().getMetrics(dm);
|
|
86
|
mScreenWidth = dm.widthPixels;// 根据当前屏幕信息拿到屏幕的宽高
|
|
87
|
mScreenHeight = dm.heightPixels - getStatusBarHeight();
|
|
88
|
this.mWmParams = new WindowManager.LayoutParams();// 获取窗口参数
|
|
89
|
|
|
90
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
|
91
|
mWmParams.type = WindowManager.LayoutParams.TYPE_TOAST;// 等于API19或API19以下需要指定窗口参数type值为TYPE_TOAST才可以作为悬浮控件显示出来
|
|
92
|
} else {
|
|
93
|
mWmParams.type = WindowManager.LayoutParams.TYPE_PHONE;// API19以上侧只需指定为TYPE_PHONE即可
|
|
94
|
}
|
|
95
|
mWmParams.format = PixelFormat.RGBA_8888;// 当前窗口的像素格式为RGBA_8888,即为最高质量
|
|
96
|
|
|
97
|
// NOT_FOCUSABLE可以是悬浮控件可以响应事件,LAYOUT_IN_SCREEN可以指定悬浮球指定在屏幕内,部分虚拟按键的手机,虚拟按键隐藏时,虚拟按键的位置则属于屏幕内,此时悬浮球会出现在原虚拟按键的位置
|
|
98
|
mWmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
|
99
|
// 默认指定位置在屏幕的左上方,可以根据需要自己修改
|
|
100
|
mWmParams.gravity = Gravity.LEFT | Gravity.TOP;
|
|
101
|
position = 1;
|
|
102
|
// 默认指定的横坐标为屏幕一半
|
|
103
|
mWmParams.x = mScreenWidth / 2;
|
|
104
|
// 默认指定的纵坐标为屏幕高度的一半,这里只是大概约束,因为上的flags参数限制,悬浮球不会出现在屏幕外
|
|
105
|
mWmParams.y = mScreenHeight / 2;
|
|
106
|
|
|
107
|
// 宽度指定为内容自适应
|
|
108
|
mWmParams.width = LayoutParams.WRAP_CONTENT;
|
|
109
|
mWmParams.height = LayoutParams.WRAP_CONTENT;
|
|
110
|
|
|
111
|
mPathMenuLayout = (PathMenuLayout) findViewById(getIdByName(context, "id", "item_layout"));
|
|
112
|
|
|
113
|
controlLayout = (FrameLayout) findViewById(getIdByName(context, "id", "control_layout"));
|
|
114
|
controlLayout.setClickable(true);
|
|
115
|
controlLayout.setOnTouchListener(this);
|
|
116
|
|
|
117
|
mHintView = (ImageView) findViewById(getIdByName(context, "id", "control_hint"));
|
|
118
|
|
|
119
|
mWindowManager.addView(this, mWmParams);
|
|
120
|
|
|
121
|
controlLayout.setOnClickListener(new OnClickListener() {
|
|
122
|
@Override
|
|
123
|
public void onClick(View v) {
|
|
124
|
if (!mDraging) {
|
|
125
|
if (mPathMenuLayout.isExpanded()) {
|
|
126
|
mPathMenuLayout.switchState(true, position);
|
|
127
|
mHintView
|
|
128
|
.startAnimation(createHintSwitchAnimation(true));
|
|
129
|
} else {
|
|
130
|
mHintView
|
|
131
|
.startAnimation(createHintSwitchAnimation(false));
|
|
132
|
mPathMenuLayout.setVisibility(VISIBLE);
|
|
133
|
mPathMenuLayout.setPosition(position);
|
|
134
|
postDelayed(new Runnable() {
|
|
135
|
@Override
|
|
136
|
public void run() {
|
|
137
|
mPathMenuLayout.switchState(true);
|
|
138
|
}
|
|
139
|
}, 60);
|
|
140
|
|
|
141
|
}
|
|
142
|
}
|
|
143
|
}
|
|
144
|
});
|
|
145
|
// 必须在绘制完成后才能获得控件的宽高
|
|
146
|
ViewTreeObserver vto = controlLayout.getViewTreeObserver();
|
|
147
|
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
|
|
148
|
@Override
|
|
149
|
public void onGlobalLayout() {
|
|
150
|
controlLayout.getViewTreeObserver()
|
|
151
|
.removeGlobalOnLayoutListener(this);
|
|
152
|
int controlLayoutWidth = controlLayout.getWidth();
|
|
153
|
mPathMenuLayout.setChildSize(controlLayoutWidth);
|
|
154
|
}
|
|
155
|
});
|
|
156
|
}
|
|
157
|
|
|
158
|
|
|
159
|
public static int getIdByName(Context context, String className, String name) {
|
|
160
|
String packageName = context.getPackageName();
|
|
161
|
Class r = null;
|
|
162
|
int id = 0;
|
|
163
|
|
|
164
|
try {
|
|
165
|
r = Class.forName(packageName + ".R");
|
|
166
|
Class[] classes = r.getClasses();
|
|
167
|
Class desireClass = null;
|
|
168
|
|
|
169
|
for (int i = 0; i < classes.length; ++i) {
|
|
170
|
if (classes[i].getName().split("\\$")[1].equals(className)) {
|
|
171
|
desireClass = classes[i];
|
|
172
|
break;
|
|
173
|
}
|
|
174
|
}
|
|
175
|
|
|
176
|
if (desireClass != null) {
|
|
177
|
id = desireClass.getField(name).getInt(desireClass);
|
|
178
|
}
|
|
179
|
} catch (ClassNotFoundException e) {
|
|
180
|
e.printStackTrace();
|
|
181
|
} catch (IllegalArgumentException e) {
|
|
182
|
e.printStackTrace();
|
|
183
|
} catch (SecurityException e) {
|
|
184
|
e.printStackTrace();
|
|
185
|
} catch (IllegalAccessException e) {
|
|
186
|
e.printStackTrace();
|
|
187
|
} catch (NoSuchFieldException e) {
|
|
188
|
e.printStackTrace();
|
|
189
|
}
|
|
190
|
|
|
191
|
return id;
|
|
192
|
}
|
|
193
|
/**
|
|
194
|
* 应用自定义属性,设置弧度、子菜单项大小
|
|
195
|
*
|
|
196
|
* @param attrs
|
|
197
|
*/
|
|
198
|
private void applyAttrs(AttributeSet attrs) {
|
|
199
|
if (attrs != null) {
|
|
200
|
TypedArray a = getContext().obtainStyledAttributes(attrs,
|
|
201
|
R.styleable.ArcLayout, 0, 0);
|
|
202
|
|
|
203
|
float fromDegrees = a.getFloat(R.styleable.ArcLayout_fromDegrees,
|
|
204
|
PathMenuLayout.DEFAULT_FROM_DEGREES);
|
|
205
|
float toDegrees = a.getFloat(R.styleable.ArcLayout_toDegrees,
|
|
206
|
PathMenuLayout.DEFAULT_TO_DEGREES);
|
|
207
|
mPathMenuLayout.setArc(fromDegrees, toDegrees);
|
|
208
|
a.recycle();
|
|
209
|
}
|
|
210
|
}
|
|
211
|
|
|
212
|
/**
|
|
213
|
* 添加子菜单项和对应的点击事件
|
|
214
|
*
|
|
215
|
* @param item
|
|
216
|
* @param listener
|
|
217
|
*/
|
|
218
|
public void addItem(View item, OnClickListener listener) {
|
|
219
|
mPathMenuLayout.addView(item);
|
|
220
|
item.setOnClickListener(getItemClickListener(listener));
|
|
221
|
}
|
|
222
|
|
|
223
|
/**
|
|
224
|
* 子菜单项的点击事件
|
|
225
|
*
|
|
226
|
* @param listener
|
|
227
|
* @return
|
|
228
|
*/
|
|
229
|
private OnClickListener getItemClickListener(final OnClickListener listener) {
|
|
230
|
return new OnClickListener() {
|
|
231
|
// 点击,child放大,其他child消失
|
|
232
|
@Override
|
|
233
|
public void onClick(final View viewClicked) {
|
|
234
|
Animation animation = bindItemAnimation(viewClicked, true, 400);
|
|
235
|
animation.setAnimationListener(new AnimationListener() {
|
|
236
|
|
|
237
|
@Override
|
|
238
|
public void onAnimationStart(Animation animation) {
|
|
239
|
|
|
240
|
}
|
|
241
|
|
|
242
|
@Override
|
|
243
|
public void onAnimationRepeat(Animation animation) {
|
|
244
|
|
|
245
|
}
|
|
246
|
|
|
247
|
@Override
|
|
248
|
public void onAnimationEnd(Animation animation) {
|
|
249
|
postDelayed(new Runnable() {
|
|
250
|
|
|
251
|
@Override
|
|
252
|
public void run() {
|
|
253
|
itemDidDisappear();
|
|
254
|
}
|
|
255
|
}, 0);
|
|
256
|
}
|
|
257
|
});
|
|
258
|
// child 被点击的时候,自身放大,其他child消失的效果
|
|
259
|
final int itemCount = mPathMenuLayout.getChildCount();
|
|
260
|
for (int i = 0; i < itemCount; i++) {
|
|
261
|
View item = mPathMenuLayout.getChildAt(i);
|
|
262
|
if (viewClicked != item) {
|
|
263
|
bindItemAnimation(item, false, 300);
|
|
264
|
}
|
|
265
|
}
|
|
266
|
// 中心控制点动画 旋转
|
|
267
|
mPathMenuLayout.invalidate();
|
|
268
|
mHintView.startAnimation(createHintSwitchAnimation(true));
|
|
269
|
|
|
270
|
if (listener != null) {
|
|
271
|
listener.onClick(viewClicked);
|
|
272
|
}
|
|
273
|
}
|
|
274
|
};
|
|
275
|
}
|
|
276
|
|
|
277
|
/**
|
|
278
|
* 绑定子菜单项动画
|
|
279
|
*
|
|
280
|
* @param child
|
|
281
|
* @param isClicked
|
|
282
|
* @param duration
|
|
283
|
* @return
|
|
284
|
*/
|
|
285
|
private Animation bindItemAnimation(final View child,
|
|
286
|
final boolean isClicked, final long duration) {
|
|
287
|
Animation animation = createItemDisapperAnimation(duration, isClicked);
|
|
288
|
child.setAnimation(animation);
|
|
289
|
|
|
290
|
return animation;
|
|
291
|
}
|
|
292
|
|
|
293
|
/**
|
|
294
|
* 子菜单项关闭时消失
|
|
295
|
*/
|
|
296
|
private void itemDidDisappear() {
|
|
297
|
final int itemCount = mPathMenuLayout.getChildCount();
|
|
298
|
for (int i = 0; i < itemCount; i++) {
|
|
299
|
View item = mPathMenuLayout.getChildAt(i);
|
|
300
|
item.clearAnimation();
|
|
301
|
}
|
|
302
|
mPathMenuLayout.switchState(false, position);
|
|
303
|
}
|
|
304
|
|
|
305
|
/**
|
|
306
|
* 子菜单消失动画
|
|
307
|
*
|
|
308
|
* @param duration
|
|
309
|
* @param isClicked
|
|
310
|
* @return
|
|
311
|
*/
|
|
312
|
private static Animation createItemDisapperAnimation(final long duration,
|
|
313
|
final boolean isClicked) {
|
|
314
|
AnimationSet animationSet = new AnimationSet(true);
|
|
315
|
// 放大缩小动画,isClicked 为true,放大两倍;为false,缩小至0
|
|
316
|
animationSet.addAnimation(new ScaleAnimation(1.0f, isClicked ? 2.0f
|
|
317
|
: 0.0f, 1.0f, isClicked ? 2.0f : 0.0f,
|
|
318
|
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
|
|
319
|
0.5f));
|
|
320
|
// Alpha改为0,child消失
|
|
321
|
animationSet.addAnimation(new AlphaAnimation(1.0f, 0.0f));
|
|
322
|
|
|
323
|
animationSet.setDuration(duration);
|
|
324
|
animationSet.setInterpolator(new DecelerateInterpolator());
|
|
325
|
animationSet.setFillAfter(true);
|
|
326
|
|
|
327
|
return animationSet;
|
|
328
|
}
|
|
329
|
|
|
330
|
|
|
331
|
/**
|
|
332
|
* 中心按钮点击旋转45度动画
|
|
333
|
*
|
|
334
|
* @param expanded
|
|
335
|
* @return
|
|
336
|
*/
|
|
337
|
private static Animation createHintSwitchAnimation(final boolean expanded) {
|
|
338
|
Animation animation = new RotateAnimation(expanded ? 45 : 0,
|
|
339
|
expanded ? 0 : 45, Animation.RELATIVE_TO_SELF, 0.5f,
|
|
340
|
Animation.RELATIVE_TO_SELF, 0.5f);
|
|
341
|
animation.setStartOffset(0);
|
|
342
|
animation.setDuration(100);
|
|
343
|
animation.setInterpolator(new DecelerateInterpolator());
|
|
344
|
animation.setFillAfter(true);
|
|
345
|
|
|
346
|
return animation;
|
|
347
|
}
|
|
348
|
|
|
349
|
/**
|
|
350
|
* 触摸事件
|
|
351
|
*/
|
|
352
|
@Override
|
|
353
|
public boolean onTouch(View v, MotionEvent event) {
|
|
354
|
float x = (int) event.getRawX();
|
|
355
|
float y = (int) event.getRawY();
|
|
356
|
switch (event.getAction()) {
|
|
357
|
case MotionEvent.ACTION_DOWN:
|
|
358
|
mTouchStartX = event.getX();
|
|
359
|
mTouchStartY = event.getY();
|
|
360
|
mWmParams.alpha = 1f;
|
|
361
|
mWindowManager.updateViewLayout(this, mWmParams);
|
|
362
|
mDraging = false;
|
|
363
|
break;
|
|
364
|
|
|
365
|
case MotionEvent.ACTION_MOVE:
|
|
366
|
float mMoveStartX = event.getX();
|
|
367
|
float mMoveStartY = event.getY();
|
|
368
|
if (Math.abs(mTouchStartX - mMoveStartX) > 2
|
|
369
|
&& Math.abs(mTouchStartY - mMoveStartY) > 2) {
|
|
370
|
mDraging = true;
|
|
371
|
mWmParams.x = (int) (x - mTouchStartX);
|
|
372
|
mWmParams.y = (int) (y - mTouchStartY);
|
|
373
|
mWindowManager.updateViewLayout(this, mWmParams);
|
|
374
|
if (mPathMenuLayout.isExpanded()) {
|
|
375
|
mHintView
|
|
376
|
.startAnimation(createHintSwitchAnimation(true));
|
|
377
|
mPathMenuLayout.switchState(true, position);
|
|
378
|
}
|
|
379
|
return false;
|
|
380
|
}
|
|
381
|
break;
|
|
382
|
|
|
383
|
case MotionEvent.ACTION_UP:
|
|
384
|
case MotionEvent.ACTION_CANCEL:
|
|
385
|
int wmX = mWmParams.x;
|
|
386
|
int wmY = mWmParams.y;
|
|
387
|
if (wmX <= mScreenWidth / 8) // 左边 竖区域
|
|
388
|
{
|
|
389
|
if (wmY <= mScreenHeight / 8) {
|
|
390
|
position = LEFT_TOP;// 左上
|
|
391
|
} else if (wmY >= mScreenHeight / 8
|
|
392
|
&& wmY <= mScreenHeight * 7 / 8) {
|
|
393
|
position = LEFT_CENTER;// 左中
|
|
394
|
} else if (wmY >= mScreenHeight * 7 / 8) {
|
|
395
|
position = LEFT_BOTTOM;// 左下
|
|
396
|
}
|
|
397
|
} else if (wmX >= mScreenWidth / 8 && wmX <= mScreenWidth * 7 / 8)// 中间
|
|
398
|
// 竖区域
|
|
399
|
{
|
|
400
|
if (wmY <= mScreenHeight / 8) {
|
|
401
|
position = CENTER_TOP;// 中上
|
|
402
|
} else if (wmY >= mScreenHeight / 8
|
|
403
|
&& wmY <= mScreenHeight * 7 / 8) {
|
|
404
|
position = CENTER;// 中
|
|
405
|
} else if (wmY >= mScreenHeight * 7 / 8) {
|
|
406
|
position = CENTER_BOTTOM;// 中下
|
|
407
|
}
|
|
408
|
}
|
|
409
|
|
|
410
|
else if (wmX >= mScreenWidth * 7 / 8)// 右边竖区域
|
|
411
|
{
|
|
412
|
if (wmY <= mScreenHeight / 8) {
|
|
413
|
position = RIGHT_TOP;// 上右
|
|
414
|
} else if (wmY >= mScreenHeight / 8
|
|
415
|
&& wmY <= mScreenHeight * 7 / 8) {
|
|
416
|
position = RIGHT_CENTER;// 中右
|
|
417
|
} else if (wmY >= mScreenHeight * 7 / 8) {
|
|
418
|
position = RIGHT_BOTTOM;// 下右
|
|
419
|
}
|
|
420
|
}
|
|
421
|
refreshPathMenu(position);
|
|
422
|
|
|
423
|
mWindowManager.updateViewLayout(this, mWmParams);
|
|
424
|
mTouchStartX = mTouchStartY = 0;
|
|
425
|
break;
|
|
426
|
}
|
|
427
|
return false;
|
|
428
|
}
|
|
429
|
|
|
430
|
/**
|
|
431
|
* 根据按钮位置改变子菜单方向
|
|
432
|
*
|
|
433
|
* @param position
|
|
434
|
*/
|
|
435
|
private void refreshPathMenu(int position) {
|
|
436
|
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mPathMenuLayout
|
|
437
|
.getLayoutParams();
|
|
438
|
FrameLayout.LayoutParams params1 = (FrameLayout.LayoutParams) controlLayout
|
|
439
|
.getLayoutParams();
|
|
440
|
|
|
441
|
switch (position) {
|
|
442
|
case PathMenu.LEFT_TOP:// 左上
|
|
443
|
params1.gravity = Gravity.LEFT | Gravity.TOP;
|
|
444
|
params.gravity = Gravity.LEFT | Gravity.TOP;
|
|
445
|
mPathMenuLayout.setArc(0, 90, position);
|
|
446
|
break;
|
|
447
|
case PathMenu.LEFT_CENTER:// 左中
|
|
448
|
params1.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
|
|
449
|
params.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
|
|
450
|
mPathMenuLayout.setArc(270, 270 + 180, position);
|
|
451
|
break;
|
|
452
|
case PathMenu.LEFT_BOTTOM:// 左下
|
|
453
|
params1.gravity = Gravity.LEFT | Gravity.BOTTOM;
|
|
454
|
params.gravity = Gravity.LEFT | Gravity.BOTTOM;
|
|
455
|
mPathMenuLayout.setArc(270, 360, position);
|
|
456
|
break;
|
|
457
|
case PathMenu.RIGHT_TOP:// 右上
|
|
458
|
params1.gravity = Gravity.RIGHT | Gravity.TOP;
|
|
459
|
params.gravity = Gravity.RIGHT | Gravity.TOP;
|
|
460
|
mPathMenuLayout.setArc(90, 180, position);
|
|
461
|
break;
|
|
462
|
case PathMenu.RIGHT_CENTER:// 右中
|
|
463
|
params1.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
|
|
464
|
params.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
|
|
465
|
mPathMenuLayout.setArc(90, 270, position);
|
|
466
|
break;
|
|
467
|
case PathMenu.RIGHT_BOTTOM:// 右下
|
|
468
|
params1.gravity = Gravity.BOTTOM | Gravity.RIGHT;
|
|
469
|
params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
|
|
470
|
mPathMenuLayout.setArc(180, 270, position);
|
|
471
|
break;
|
|
472
|
|
|
473
|
case PathMenu.CENTER_TOP:// 上中
|
|
474
|
params1.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
|
|
475
|
params.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
|
|
476
|
mPathMenuLayout.setArc(0, 180, position);
|
|
477
|
break;
|
|
478
|
case PathMenu.CENTER_BOTTOM:// 下中
|
|
479
|
params1.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
|
|
480
|
params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
|
|
481
|
mPathMenuLayout.setArc(180, 360, position);
|
|
482
|
break;
|
|
483
|
case PathMenu.CENTER:
|
|
484
|
params1.gravity = Gravity.CENTER;
|
|
485
|
params.gravity = Gravity.CENTER;
|
|
486
|
mPathMenuLayout.setArc(0, 360, position);
|
|
487
|
break;
|
|
488
|
}
|
|
489
|
controlLayout.setLayoutParams(params1);
|
|
490
|
mPathMenuLayout.setLayoutParams(params);
|
|
491
|
|
|
492
|
}
|
|
493
|
|
|
494
|
int statusBarHeight = 0;
|
|
495
|
|
|
496
|
public int getStatusBarHeight() {
|
|
497
|
if (statusBarHeight <= 0) {
|
|
498
|
int resourceId = getResources().getIdentifier("status_bar_height",
|
|
499
|
"dimen", "android");
|
|
500
|
if (resourceId > 0) {
|
|
501
|
statusBarHeight = getResources().getDimensionPixelSize(
|
|
502
|
resourceId);
|
|
503
|
}
|
|
504
|
}
|
|
505
|
return statusBarHeight;
|
|
506
|
}
|
|
507
|
|
|
508
|
public void hidePathMenu() {
|
|
509
|
try {
|
|
510
|
setVisibility(View.GONE);
|
|
511
|
} catch (final IllegalArgumentException e) {
|
|
512
|
Log.e("PathMenu", "hidePathMenu error!");
|
|
513
|
}
|
|
514
|
}
|
|
515
|
}
|
|
@ -0,0 +1,39 @@
|
|
1
|
package com.ai.ipu.ipu_pathmenu;
|
|
2
|
|
|
3
|
import android.app.Activity;
|
|
4
|
import android.content.Intent;
|
|
5
|
import android.os.Bundle;
|
|
6
|
import android.view.View;
|
|
7
|
import android.view.View.OnClickListener;
|
|
8
|
import android.widget.Button;
|
|
9
|
|
|
10
|
public class PathMenuActivity extends Activity {
|
|
11
|
|
|
12
|
@Override
|
|
13
|
protected void onCreate(Bundle savedInstanceState) {
|
|
14
|
super.onCreate(savedInstanceState);
|
|
15
|
setContentView(R.layout.activity_float);
|
|
16
|
Button btnOpen = (Button) findViewById(R.id.btnOpenFloat);
|
|
17
|
Button btnClose = (Button) findViewById(R.id.btnCloseFloat);
|
|
18
|
|
|
19
|
btnOpen.setOnClickListener(mClickListener);
|
|
20
|
btnClose.setOnClickListener(mClickListener);
|
|
21
|
|
|
22
|
}
|
|
23
|
|
|
24
|
OnClickListener mClickListener = new OnClickListener() {
|
|
25
|
|
|
26
|
@Override
|
|
27
|
public void onClick(View v) {
|
|
28
|
if (v.getId() == R.id.btnOpenFloat) {
|
|
29
|
Intent intent = new Intent(PathMenuActivity.this,
|
|
30
|
PathMenuService.class);
|
|
31
|
startService(intent);
|
|
32
|
} else if (v.getId() == R.id.btnCloseFloat) {
|
|
33
|
Intent intent = new Intent(PathMenuActivity.this,
|
|
34
|
PathMenuService.class);
|
|
35
|
stopService(intent);
|
|
36
|
}
|
|
37
|
}
|
|
38
|
};
|
|
39
|
}
|
|
@ -0,0 +1,495 @@
|
|
1
|
package com.ai.ipu.ipu_pathmenu;
|
|
2
|
|
|
3
|
import android.content.Context;
|
|
4
|
import android.content.res.TypedArray;
|
|
5
|
import android.graphics.Rect;
|
|
6
|
import android.util.AttributeSet;
|
|
7
|
import android.view.View;
|
|
8
|
import android.view.ViewGroup;
|
|
9
|
import android.view.animation.AccelerateInterpolator;
|
|
10
|
import android.view.animation.Animation;
|
|
11
|
import android.view.animation.Animation.AnimationListener;
|
|
12
|
import android.view.animation.AnimationSet;
|
|
13
|
import android.view.animation.Interpolator;
|
|
14
|
import android.view.animation.LinearInterpolator;
|
|
15
|
import android.view.animation.OvershootInterpolator;
|
|
16
|
import android.view.animation.RotateAnimation;
|
|
17
|
|
|
18
|
/**
|
|
19
|
* 子菜单项布局
|
|
20
|
*
|
|
21
|
* @author 何凌波
|
|
22
|
*
|
|
23
|
*/
|
|
24
|
public class PathMenuLayout extends ViewGroup {
|
|
25
|
private int mChildSize; // 子菜单项大小相同
|
|
26
|
private int mChildPadding = 5;
|
|
27
|
private int mLayoutPadding = 10;
|
|
28
|
public static final float DEFAULT_FROM_DEGREES = 270.0f;
|
|
29
|
public static final float DEFAULT_TO_DEGREES = 360.0f;
|
|
30
|
private float mFromDegrees = DEFAULT_FROM_DEGREES;
|
|
31
|
private float mToDegrees = DEFAULT_TO_DEGREES;
|
|
32
|
private static final int MIN_RADIUS = 100;
|
|
33
|
private int mRadius;// 中心菜单圆点到子菜单中心的距离
|
|
34
|
private boolean mExpanded = false;
|
|
35
|
private int position = PathMenu.LEFT_TOP;
|
|
36
|
private int centerX = 0;
|
|
37
|
private int centerY = 0;
|
|
38
|
|
|
39
|
public void computeCenterXY(int position) {
|
|
40
|
switch (position) {
|
|
41
|
case PathMenu.LEFT_TOP:// 左上
|
|
42
|
centerX = getWidth() / 2 - getRadiusAndPadding();
|
|
43
|
centerY = getHeight() / 2 - getRadiusAndPadding();
|
|
44
|
break;
|
|
45
|
case PathMenu.LEFT_CENTER:// 左中
|
|
46
|
centerX = getWidth() / 2 - getRadiusAndPadding();
|
|
47
|
centerY = getHeight() / 2;
|
|
48
|
break;
|
|
49
|
case PathMenu.LEFT_BOTTOM:// 左下
|
|
50
|
centerX = getWidth() / 2 - getRadiusAndPadding();
|
|
51
|
centerY = getHeight() / 2 + getRadiusAndPadding();
|
|
52
|
break;
|
|
53
|
case PathMenu.CENTER_TOP:// 上中
|
|
54
|
centerX = getWidth() / 2;
|
|
55
|
centerY = getHeight() / 2 - getRadiusAndPadding();
|
|
56
|
break;
|
|
57
|
case PathMenu.CENTER_BOTTOM:// 下中
|
|
58
|
centerX = getWidth() / 2;
|
|
59
|
centerY = getHeight() / 2 + getRadiusAndPadding();
|
|
60
|
break;
|
|
61
|
case PathMenu.RIGHT_TOP:// 右上
|
|
62
|
centerX = getWidth() / 2 + getRadiusAndPadding();
|
|
63
|
centerY = getHeight() / 2 - getRadiusAndPadding();
|
|
64
|
break;
|
|
65
|
case PathMenu.RIGHT_CENTER:// 右中
|
|
66
|
centerX = getWidth() / 2 + getRadiusAndPadding();
|
|
67
|
centerY = getHeight() / 2;
|
|
68
|
break;
|
|
69
|
case PathMenu.RIGHT_BOTTOM:// 右下
|
|
70
|
centerX = getWidth() / 2 + getRadiusAndPadding();
|
|
71
|
centerY = getHeight() / 2 + getRadiusAndPadding();
|
|
72
|
break;
|
|
73
|
|
|
74
|
case PathMenu.CENTER:
|
|
75
|
centerX = getWidth() / 2;
|
|
76
|
centerY = getHeight() / 2;
|
|
77
|
break;
|
|
78
|
}
|
|
79
|
}
|
|
80
|
|
|
81
|
private int getRadiusAndPadding() {
|
|
82
|
return mRadius + (mChildPadding * 2);
|
|
83
|
}
|
|
84
|
|
|
85
|
public PathMenuLayout(Context context, AttributeSet attrs) {
|
|
86
|
super(context, attrs);
|
|
87
|
// 获取自定义属性,设定默认值
|
|
88
|
if (attrs != null) {
|
|
89
|
TypedArray a = getContext().obtainStyledAttributes(attrs,
|
|
90
|
R.styleable.ArcLayout, 0, 0);
|
|
91
|
mFromDegrees = a.getFloat(R.styleable.ArcLayout_fromDegrees,
|
|
92
|
DEFAULT_FROM_DEGREES);
|
|
93
|
mToDegrees = a.getFloat(R.styleable.ArcLayout_toDegrees,
|
|
94
|
DEFAULT_TO_DEGREES);
|
|
95
|
mChildSize = Math
|
|
96
|
.max(a.getDimensionPixelSize(
|
|
97
|
R.styleable.ArcLayout_childSize, 0), 0);
|
|
98
|
|
|
99
|
a.recycle();
|
|
100
|
}
|
|
101
|
}
|
|
102
|
|
|
103
|
/**
|
|
104
|
* 计算半径
|
|
105
|
*
|
|
106
|
* @param arcDegrees
|
|
107
|
* @param childCount
|
|
108
|
* @param childSize
|
|
109
|
* @param childPadding
|
|
110
|
* @param minRadius
|
|
111
|
* @return
|
|
112
|
*/
|
|
113
|
private static int computeRadius(final float arcDegrees,
|
|
114
|
final int childCount, final int childSize, final int childPadding,
|
|
115
|
final int minRadius) {
|
|
116
|
if (childCount < 2) {
|
|
117
|
return minRadius;
|
|
118
|
}
|
|
119
|
final float perDegrees;
|
|
120
|
if (arcDegrees < 360) {
|
|
121
|
perDegrees = arcDegrees / (childCount - 1);
|
|
122
|
} else {
|
|
123
|
perDegrees = arcDegrees / (childCount);
|
|
124
|
}
|
|
125
|
|
|
126
|
final float perHalfDegrees = perDegrees / 2;
|
|
127
|
final int perSize = childSize + childPadding;
|
|
128
|
|
|
129
|
final int radius = (int) ((perSize / 2) / Math.sin(Math
|
|
130
|
.toRadians(perHalfDegrees)));
|
|
131
|
|
|
132
|
return Math.max(radius, minRadius);
|
|
133
|
}
|
|
134
|
|
|
135
|
/**
|
|
136
|
* 计算子菜单项的范围
|
|
137
|
*
|
|
138
|
* @param centerX
|
|
139
|
* @param centerY
|
|
140
|
* @param radius
|
|
141
|
* @param degrees
|
|
142
|
* @param size
|
|
143
|
* @return
|
|
144
|
*/
|
|
145
|
private static Rect computeChildFrame(final int centerX, final int centerY,
|
|
146
|
final int radius, final float degrees, final int size) {
|
|
147
|
// 子菜单项中心点
|
|
148
|
final double childCenterX = centerX + radius
|
|
149
|
* Math.cos(Math.toRadians(degrees));
|
|
150
|
final double childCenterY = centerY + radius
|
|
151
|
* Math.sin(Math.toRadians(degrees));
|
|
152
|
// 子菜单项的左上角,右上角,左下角,右下角
|
|
153
|
return new Rect((int) (childCenterX - size / 2),
|
|
154
|
(int) (childCenterY - size / 2),
|
|
155
|
(int) (childCenterX + size / 2),
|
|
156
|
(int) (childCenterY + size / 2));
|
|
157
|
}
|
|
158
|
|
|
159
|
/**
|
|
160
|
* 子菜单项大小
|
|
161
|
*/
|
|
162
|
@Override
|
|
163
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
164
|
int radius = mRadius = computeRadius(
|
|
165
|
Math.abs(mToDegrees - mFromDegrees), getChildCount(),
|
|
166
|
mChildSize, mChildPadding, MIN_RADIUS);
|
|
167
|
|
|
168
|
int size = radius * 2 + mChildSize + mChildPadding + mLayoutPadding * 2;
|
|
169
|
|
|
170
|
setMeasuredDimension(size, size);
|
|
171
|
|
|
172
|
final int count = getChildCount();
|
|
173
|
for (int i = 0; i < count; i++) {
|
|
174
|
getChildAt(i)
|
|
175
|
.measure(
|
|
176
|
MeasureSpec.makeMeasureSpec(mChildSize,
|
|
177
|
MeasureSpec.EXACTLY),
|
|
178
|
MeasureSpec.makeMeasureSpec(mChildSize,
|
|
179
|
MeasureSpec.EXACTLY));
|
|
180
|
}
|
|
181
|
}
|
|
182
|
|
|
183
|
/**
|
|
184
|
* 子菜单项位置
|
|
185
|
*/
|
|
186
|
@Override
|
|
187
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
|
188
|
computeCenterXY(position);
|
|
189
|
// 当子菜单要收缩时radius=0,在ViewGroup坐标中心
|
|
190
|
final int radius = mExpanded ? mRadius : 0;
|
|
191
|
final float perDegrees;// 菜单项之间的角度
|
|
192
|
final int childCount = getChildCount();
|
|
193
|
if (mToDegrees - mFromDegrees >= 360) {
|
|
194
|
perDegrees = (mToDegrees - mFromDegrees) / (childCount);
|
|
195
|
} else {
|
|
196
|
perDegrees = (mToDegrees - mFromDegrees) / (childCount - 1);
|
|
197
|
}
|
|
198
|
|
|
199
|
float degrees = mFromDegrees;
|
|
200
|
for (int i = 0; i < childCount; i++) {
|
|
201
|
Rect frame = computeChildFrame(centerX, centerY, radius, degrees,
|
|
202
|
mChildSize);
|
|
203
|
degrees += perDegrees;
|
|
204
|
getChildAt(i).layout(frame.left, frame.top, frame.right,
|
|
205
|
frame.bottom);
|
|
206
|
}
|
|
207
|
}
|
|
208
|
|
|
209
|
/**
|
|
210
|
* 计算动画开始时的偏移量
|
|
211
|
*
|
|
212
|
* @param childCount
|
|
213
|
* @param expanded
|
|
214
|
* @param index
|
|
215
|
* @param delayPercent
|
|
216
|
* @param duration
|
|
217
|
* @param interpolator
|
|
218
|
* @return
|
|
219
|
*/
|
|
220
|
private static long computeStartOffset(final int childCount,
|
|
221
|
final boolean expanded, final int index, final float delayPercent,
|
|
222
|
final long duration, Interpolator interpolator) {
|
|
223
|
final float delay = delayPercent * duration;
|
|
224
|
final long viewDelay = (long) (getTransformedIndex(expanded,
|
|
225
|
childCount, index) * delay);
|
|
226
|
final float totalDelay = delay * childCount;
|
|
227
|
|
|
228
|
float normalizedDelay = viewDelay / totalDelay;
|
|
229
|
normalizedDelay = interpolator.getInterpolation(normalizedDelay);
|
|
230
|
|
|
231
|
return (long) (normalizedDelay * totalDelay);
|
|
232
|
}
|
|
233
|
|
|
234
|
/**
|
|
235
|
* 变换时的子菜单项索引
|
|
236
|
*
|
|
237
|
* @param expanded
|
|
238
|
* @param count
|
|
239
|
* @param index
|
|
240
|
* @return
|
|
241
|
*/
|
|
242
|
private static int getTransformedIndex(final boolean expanded,
|
|
243
|
final int count, final int index) {
|
|
244
|
if (expanded) {
|
|
245
|
return count - 1 - index;
|
|
246
|
}
|
|
247
|
|
|
248
|
return index;
|
|
249
|
}
|
|
250
|
|
|
251
|
/**
|
|
252
|
* 展开动画
|
|
253
|
*
|
|
254
|
* @param fromXDelta
|
|
255
|
* @param toXDelta
|
|
256
|
* @param fromYDelta
|
|
257
|
* @param toYDelta
|
|
258
|
* @param startOffset
|
|
259
|
* @param duration
|
|
260
|
* @param interpolator
|
|
261
|
* @return
|
|
262
|
*/
|
|
263
|
private static Animation createExpandAnimation(float fromXDelta,
|
|
264
|
float toXDelta, float fromYDelta, float toYDelta, long startOffset,
|
|
265
|
long duration, Interpolator interpolator) {
|
|
266
|
Animation animation = new RotateAndTranslateAnimation(0, toXDelta, 0,
|
|
267
|
toYDelta, 0, 720);
|
|
268
|
animation.setStartOffset(startOffset);
|
|
269
|
animation.setDuration(duration);
|
|
270
|
animation.setInterpolator(interpolator);
|
|
271
|
animation.setFillAfter(true);
|
|
272
|
|
|
273
|
return animation;
|
|
274
|
}
|
|
275
|
|
|
276
|
/**
|
|
277
|
* 收缩动画
|
|
278
|
*
|
|
279
|
* @param fromXDelta
|
|
280
|
* @param toXDelta
|
|
281
|
* @param fromYDelta
|
|
282
|
* @param toYDelta
|
|
283
|
* @param startOffset
|
|
284
|
* @param duration
|
|
285
|
* @param interpolator
|
|
286
|
* @return
|
|
287
|
*/
|
|
288
|
private static Animation createShrinkAnimation(float fromXDelta,
|
|
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
|
}
|
|
@ -0,0 +1,28 @@
|
|
1
|
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
xmlns:arc="http://schemas.android.com/apk/res-auto"
|
|
4
|
android:layout_width="fill_parent"
|
|
5
|
android:layout_height="fill_parent" >
|
|
6
|
|
|
7
|
<LinearLayout
|
|
8
|
android:layout_width="fill_parent"
|
|
9
|
android:layout_height="wrap_content"
|
|
10
|
android:orientation="vertical" >
|
|
11
|
|
|
12
|
<Button
|
|
13
|
android:id="@+id/btnOpenFloat"
|
|
14
|
android:layout_width="wrap_content"
|
|
15
|
android:layout_height="wrap_content"
|
|
16
|
android:layout_weight="1"
|
|
17
|
android:text="open float menu" />
|
|
18
|
|
|
19
|
<Button
|
|
20
|
android:id="@+id/btnCloseFloat"
|
|
21
|
android:layout_width="wrap_content"
|
|
22
|
android:layout_height="wrap_content"
|
|
23
|
android:layout_weight="1"
|
|
24
|
android:text="close float menu" />
|
|
25
|
|
|
26
|
</LinearLayout>
|
|
27
|
|
|
28
|
</ScrollView>
|
|
@ -0,0 +1,29 @@
|
|
1
|
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
|
|
3
|
|
|
4
|
<com.ai.ipu.ipu_pathmenu.PathMenuLayout
|
|
5
|
xmlns:custom="http://schemas.android.com/apk/res-auto"
|
|
6
|
android:id="@+id/item_layout"
|
|
7
|
android:layout_width="wrap_content"
|
|
8
|
android:layout_height="wrap_content"
|
|
9
|
android:layout_centerInParent="true"
|
|
10
|
custom:fromDegrees="270.0"
|
|
11
|
custom:toDegrees="360.0" />
|
|
12
|
|
|
13
|
<FrameLayout
|
|
14
|
android:id="@+id/control_layout"
|
|
15
|
android:layout_width="wrap_content"
|
|
16
|
android:layout_height="wrap_content"
|
|
17
|
android:layout_centerInParent="true"
|
|
18
|
android:background="@drawable/composer_button" >
|
|
19
|
|
|
20
|
<ImageView
|
|
21
|
android:id="@+id/control_hint"
|
|
22
|
android:layout_width="wrap_content"
|
|
23
|
android:layout_height="wrap_content"
|
|
24
|
android:layout_gravity="center"
|
|
25
|
android:duplicateParentState="true"
|
|
26
|
android:src="@drawable/composer_icn_plus" />
|
|
27
|
</FrameLayout>
|
|
28
|
|
|
29
|
</merge>
|
|
@ -1,78 +0,0 @@
|
1
|
|
package com.wade.mobile.func;
|
2
|
|
|
3
|
|
import java.util.Map;
|
4
|
|
|
5
|
|
import android.view.KeyEvent;
|
6
|
|
import android.webkit.WebView;
|
7
|
|
import android.widget.LinearLayout;
|
8
|
|
|
9
|
|
import com.ai.ipu.mobile.util.IpuMobileUtility;
|
10
|
|
import com.ailk.common.data.impl.DataMap;
|
11
|
|
import com.wade.mobile.frame.IWadeMobile;
|
12
|
|
import com.wade.mobile.frame.activity.WadeMobileActivity;
|
13
|
|
import com.wade.mobile.frame.client.WadeWebViewClient;
|
14
|
|
import com.wade.mobile.frame.config.ServerPageConfig;
|
15
|
|
import com.wade.mobile.frame.event.impl.TemplateWebViewEvent;
|
16
|
|
import com.wade.mobile.frame.template.TemplateWebView;
|
17
|
|
import com.wade.mobile.ui.anim.AnimationResource;
|
18
|
|
import com.wade.mobile.ui.view.FlipperLayout;
|
19
|
|
import com.wade.mobile.util.Messages;
|
20
|
|
|
21
|
|
/**
|
22
|
|
* IpuWindowWebView用于替换CustomWindowActivity,保留openWindow和closeWindow的API使用
|
23
|
|
*/
|
24
|
|
public class IpuWindowWebView extends TemplateWebView {
|
25
|
|
|
26
|
|
public IpuWindowWebView(IWadeMobile wademobile) {
|
27
|
|
super(wademobile);
|
28
|
|
}
|
29
|
|
|
30
|
|
@Override
|
31
|
|
protected void initialize() {
|
32
|
|
setWebViewClient(new WadeWebViewClient(wademobile,
|
33
|
|
new TemplateWebViewEvent(wademobile) {
|
34
|
|
@Override
|
35
|
|
public void loadingFinished(WebView view, String url) {
|
36
|
|
wademobile.getFlipperLayout().showNextView();
|
37
|
|
}
|
38
|
|
}));
|
39
|
|
clearCache(true);
|
40
|
|
setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
|
41
|
|
((WadeMobileActivity) wademobile).getWebviewSetting().setWebViewStyle(this);
|
42
|
|
}
|
43
|
|
|
44
|
|
public void openWindow(String pageAction, String data) throws Exception {
|
45
|
|
/* 获取模板相对路径 */
|
46
|
|
final String templatePath = ServerPageConfig.getTemplate(pageAction);
|
47
|
|
if (templatePath == null || "".equals(templatePath)) {
|
48
|
|
IpuMobileUtility.error(Messages.NO_TEMPLATE + ",Action:" + pageAction);
|
49
|
|
}
|
50
|
|
|
51
|
|
Map<?, ?> dataMap = null;
|
52
|
|
if(data != null) {
|
53
|
|
dataMap = new DataMap(data.replaceAll("\"null\"", "\"\"").replaceAll("null", "\"\""));
|
54
|
|
}
|
55
|
|
FlipperLayout flipperLayout = wademobile.getFlipperLayout();
|
56
|
|
flipperLayout.setAnimation(AnimationResource.pushLeft[0],
|
57
|
|
AnimationResource.pushLeft[1]);
|
58
|
|
flipperLayout.setBackAnimation(AnimationResource.pushRight[0],
|
59
|
|
AnimationResource.pushRight[1]);
|
60
|
|
flipperLayout.addNextView(this);// 增加view
|
61
|
|
flipperLayout.setPreCurrView(this);
|
62
|
|
loadTemplate(templatePath, dataMap);
|
63
|
|
|
64
|
|
}
|
65
|
|
|
66
|
|
public void closeWindow(String resultData) {
|
67
|
|
wademobile.getFlipperLayout().back();
|
68
|
|
}
|
69
|
|
|
70
|
|
@Override
|
71
|
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
72
|
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
73
|
|
closeWindow(null);
|
74
|
|
return true;
|
75
|
|
}
|
76
|
|
return super.onKeyDown(keyCode, event);
|
77
|
|
}
|
78
|
|
}
|
|
@ -0,0 +1,52 @@
|
|
1
|
package com.wade.mobile.func;
|
|
2
|
|
|
3
|
import org.json.JSONArray;
|
|
4
|
import org.json.JSONObject;
|
|
5
|
|
|
6
|
import android.content.Intent;
|
|
7
|
|
|
8
|
import com.ai.ipu.ipu_pathmenu.PathMenu;
|
|
9
|
import com.ai.ipu.ipu_pathmenu.PathMenuService;
|
|
10
|
import com.wade.mobile.frame.IWadeMobile;
|
|
11
|
import com.wade.mobile.frame.plugin.Plugin;
|
|
12
|
|
|
13
|
public class MobilePathMenu extends Plugin {
|
|
14
|
public MobilePathMenu(IWadeMobile wademobile) {
|
|
15
|
super(wademobile);
|
|
16
|
}
|
|
17
|
|
|
18
|
/**
|
|
19
|
* js传参数,调用原生方法
|
|
20
|
*
|
|
21
|
* @param menuIcons
|
|
22
|
* @throws Exception
|
|
23
|
*/
|
|
24
|
public void openPathMenu(JSONArray param) throws Exception {
|
|
25
|
|
|
26
|
JSONObject jsonObject = param.getJSONObject(0);
|
|
27
|
JSONObject map = jsonObject.getJSONObject("map");
|
|
28
|
int[] menuIcons = new int[map.length()];
|
|
29
|
menuIcons[0] = map.getInt("composer_close");
|
|
30
|
menuIcons[1] = map.getInt("composer_music");
|
|
31
|
menuIcons[2] = map.getInt("composer_place");
|
|
32
|
menuIcons[3] = map.getInt("composer_sleep");
|
|
33
|
menuIcons[4] = map.getInt("composer_thought");
|
|
34
|
menuIcons[5] = map.getInt("composer_with");
|
|
35
|
openPathMenu(menuIcons);
|
|
36
|
}
|
|
37
|
|
|
38
|
public void openPathMenu(int[] menuIcons) {
|
|
39
|
Intent intent = new Intent(context, PathMenuService.class);
|
|
40
|
intent.putExtra("icons", menuIcons);
|
|
41
|
context.startService(intent);
|
|
42
|
}
|
|
43
|
|
|
44
|
public void closePathMenu(JSONArray param) throws Exception {
|
|
45
|
closePathMenu();
|
|
46
|
}
|
|
47
|
|
|
48
|
public void closePathMenu() {
|
|
49
|
Intent intent = new Intent(context, PathMenuService.class);
|
|
50
|
context.stopService(intent);
|
|
51
|
}
|
|
52
|
}
|
|
@ -1,15 +1,5 @@
|
1
|
1
|
package com.wade.mobile.func;
|
2
|
2
|
|
3
|
|
import java.net.URLDecoder;
|
4
|
|
import java.text.ParseException;
|
5
|
|
import java.text.SimpleDateFormat;
|
6
|
|
import java.util.Calendar;
|
7
|
|
import java.util.Date;
|
8
|
|
import java.util.Map;
|
9
|
|
import java.util.Stack;
|
10
|
|
|
11
|
|
import org.json.JSONArray;
|
12
|
|
|
13
|
3
|
import android.app.AlertDialog;
|
14
|
4
|
import android.app.AlertDialog.Builder;
|
15
|
5
|
import android.app.DatePickerDialog;
|
|
@ -23,7 +13,6 @@ import android.webkit.WebView;
|
23
|
13
|
import android.widget.DatePicker;
|
24
|
14
|
import android.widget.LinearLayout;
|
25
|
15
|
import android.widget.TimePicker;
|
26
|
|
|
27
|
16
|
import com.ai.ipu.mobile.app.AppInfoUtil;
|
28
|
17
|
import com.ai.ipu.mobile.ui.HintUtil;
|
29
|
18
|
import com.ai.ipu.mobile.ui.UiTool;
|
|
@ -56,6 +45,15 @@ import com.wade.mobile.ui.layout.ConstantParams;
|
56
|
45
|
import com.wade.mobile.ui.view.FlipperLayout;
|
57
|
46
|
import com.wade.mobile.util.Constant;
|
58
|
47
|
import com.wade.mobile.util.Messages;
|
|
48
|
import org.json.JSONArray;
|
|
49
|
|
|
50
|
import java.net.URLDecoder;
|
|
51
|
import java.text.ParseException;
|
|
52
|
import java.text.SimpleDateFormat;
|
|
53
|
import java.util.Calendar;
|
|
54
|
import java.util.Date;
|
|
55
|
import java.util.Map;
|
|
56
|
import java.util.Stack;
|
59
|
57
|
|
60
|
58
|
public class MobileUI extends Plugin {
|
61
|
59
|
private static SimpleProgressDialog progressDialog = null;
|
|
@ -791,23 +789,9 @@ public class MobileUI extends Plugin {
|
791
|
789
|
public void openWindow(JSONArray param) throws Exception {
|
792
|
790
|
final String pageAction = param.getString(0);
|
793
|
791
|
final String data = param.getString(1);
|
794
|
|
/*放弃使用CustomWindowActivity*/
|
795
|
|
/*Intent intent = new Intent(context, CustomWindowActivity.class);
|
796
|
|
intent.putExtra(CustomWindowActivity.KEY_PAGE_ACTION, pageAction);
|
797
|
|
intent.putExtra(CustomWindowActivity.KEY_DATA, data);
|
798
|
|
startActivityForResult(intent, REQUEST_CODE_CUSTOM_WINDOW);*/
|
799
|
|
|
800
|
|
context.runOnUiThread(new Runnable() {
|
801
|
|
@Override
|
802
|
|
public void run() {
|
803
|
|
IpuWindowWebView webView = new IpuWindowWebView(wademobile);
|
804
|
|
try {
|
805
|
|
webView.openWindow(pageAction, data);
|
806
|
|
} catch (Exception e) {
|
807
|
|
e.printStackTrace();
|
808
|
|
}
|
809
|
|
}
|
810
|
|
});
|
|
792
|
|
|
793
|
openTemplate(pageAction, isNull(data) ? null : new DataMap(data),
|
|
794
|
true);
|
811
|
795
|
}
|
812
|
796
|
|
813
|
797
|
/**
|
|
@ -820,20 +804,10 @@ public class MobileUI extends Plugin {
|
820
|
804
|
|
821
|
805
|
@Override
|
822
|
806
|
public void run() {
|
823
|
|
IpuWindowWebView webView = (IpuWindowWebView) wademobile.getFlipperLayout().getCurrView();
|
824
|
|
webView.closeWindow(resultData);
|
|
807
|
wademobile.getFlipperLayout().back();
|
825
|
808
|
plugin.callback(resultData);
|
826
|
809
|
}
|
827
|
810
|
});
|
828
|
|
/*放弃使用CustomWindowActivity*/
|
829
|
|
/*int resultState = isNull(param.getString(1)) ? CustomWindowActivity.SUCCESS_CODE
|
830
|
|
: param.getInt(1);
|
831
|
|
if (context instanceof CustomWindowActivity) {
|
832
|
|
((CustomWindowActivity) context).closeWindow(resultData,
|
833
|
|
resultState);
|
834
|
|
} else {
|
835
|
|
HintUtil.alert(context, "无窗口可以关闭!");
|
836
|
|
}*/
|
837
|
811
|
}
|
838
|
812
|
|
839
|
813
|
@Override
|
|
@ -0,0 +1,273 @@
|
|
1
|
package com.wade.mobile.func;
|
|
2
|
import org.json.JSONArray;
|
|
3
|
import org.json.JSONObject;
|
|
4
|
import android.graphics.Color;
|
|
5
|
import android.os.CountDownTimer;
|
|
6
|
import cn.pedant.SweetAlert.SweetAlertDialog;
|
|
7
|
import com.wade.mobile.frame.IWadeMobile;
|
|
8
|
import com.wade.mobile.frame.plugin.Plugin;
|
|
9
|
/**
|
|
10
|
* 提示对话框,用到SweetAlertDialog开源库
|
|
11
|
*
|
|
12
|
* @author 何凌波
|
|
13
|
*
|
|
14
|
*/
|
|
15
|
public class MobileUIWithSAD extends Plugin {
|
|
16
|
private int i = -1;
|
|
17
|
public static final int NORMAL_TYPE = 0;// 文字提示框
|
|
18
|
public static final int ERROR_TYPE = 1;// 错误提示框
|
|
19
|
public static final int SUCCESS_TYPE = 2;// 成功提示框
|
|
20
|
public static final int WARNING_TYPE = 3;// 确认提示框
|
|
21
|
public static final int CUSTOM_IMAGE_TYPE = 4;// 自定义图片提示框
|
|
22
|
public static final int PROGRESS_TYPE = 5;// 进度条提示框
|
|
23
|
// 进度条加载时变换的颜色
|
|
24
|
public static final int blue_btn_bg_color = Color.parseColor("#AEDEF4");
|
|
25
|
public static final int material_deep_teal_50 = Color
|
|
26
|
.parseColor("#ff009688");
|
|
27
|
public static final int success_stroke_color = Color.parseColor("#F27474");
|
|
28
|
public static final int material_deep_teal_20 = Color
|
|
29
|
.parseColor("#ff80cbc4");
|
|
30
|
public static final int material_blue_grey_80 = Color
|
|
31
|
.parseColor("#ff37474f");
|
|
32
|
public static final int warning_stroke_color = Color.parseColor("#F8BB86");
|
|
33
|
public MobileUIWithSAD(IWadeMobile wademobile) {
|
|
34
|
super(wademobile);
|
|
35
|
}
|
|
36
|
/**
|
|
37
|
* js传参数,调用原生方法
|
|
38
|
*
|
|
39
|
* @param param
|
|
40
|
* @throws Exception
|
|
41
|
*/
|
|
42
|
public void alert(JSONArray param) throws Exception {
|
|
43
|
JSONObject jsonObject = param.getJSONObject(0);
|
|
44
|
JSONObject map = jsonObject.getJSONObject("map");
|
|
45
|
String content = map.getString("content");
|
|
46
|
String title = map.getString("title");
|
|
47
|
Boolean cancelable = map.getBoolean("cancelable");
|
|
48
|
int alertType = map.getInt("alertType");
|
|
49
|
int imageID = map.getInt("imageID");
|
|
50
|
alert(content, title, cancelable, alertType, imageID);
|
|
51
|
}
|
|
52
|
/**
|
|
53
|
* 普通类型的几种提示框
|
|
54
|
*
|
|
55
|
* @param content
|
|
56
|
* 文本
|
|
57
|
* @param title
|
|
58
|
* 标题
|
|
59
|
* @param cancelable
|
|
60
|
* 是否可取消
|
|
61
|
* @param alertType
|
|
62
|
* 提示框类型
|
|
63
|
* @param imageID
|
|
64
|
* 图片资源ID
|
|
65
|
* @throws Exception
|
|
66
|
*/
|
|
67
|
public void alert(String content, String title, boolean cancelable,
|
|
68
|
int alertType, int imageID) throws Exception {
|
|
69
|
final SweetAlertDialog dialog = new SweetAlertDialog(context, alertType);
|
|
70
|
if (title != null) {
|
|
71
|
dialog.setTitleText(title);
|
|
72
|
}
|
|
73
|
if (!content.equals("")) {
|
|
74
|
dialog.setContentText(content);
|
|
75
|
}else
|
|
76
|
{
|
|
77
|
dialog.setContentText(null);
|
|
78
|
}
|
|
79
|
if (imageID != 0) {
|
|
80
|
dialog.setCustomImage(imageID);
|
|
81
|
} else {
|
|
82
|
dialog.setCustomImage(null);
|
|
83
|
}
|
|
84
|
dialog.setCancelable(cancelable);
|
|
85
|
dialog.show();
|
|
86
|
}
|
|
87
|
/**
|
|
88
|
* js传参数,调用原生方法
|
|
89
|
*
|
|
90
|
* @param param
|
|
91
|
* @throws Exception
|
|
92
|
*/
|
|
93
|
public void confirm(JSONArray param) throws Exception {
|
|
94
|
JSONObject jsonObject = param.getJSONObject(0);
|
|
95
|
JSONObject map = jsonObject.getJSONObject("map");
|
|
96
|
String content = map.getString("content");
|
|
97
|
String title = map.getString("title");
|
|
98
|
Boolean cancelable = map.getBoolean("cancelable");
|
|
99
|
String cancelText = map.getString("cancelText");
|
|
100
|
String confirmText = map.getString("confirmText");
|
|
101
|
String cancelEvent = map.getString("cancelEvent");
|
|
102
|
String confirmEvent = map.getString("confirmEvent");
|
|
103
|
Boolean isCancel = map.getBoolean("isCancel");
|
|
104
|
confirm(content, title, cancelable, cancelText, confirmText, isCancel,
|
|
105
|
cancelEvent, confirmEvent);
|
|
106
|
}
|
|
107
|
/**
|
|
108
|
* 确认-取消提示框
|
|
109
|
*
|
|
110
|
* @param content
|
|
111
|
* 文本
|
|
112
|
* @param title
|
|
113
|
* 标题
|
|
114
|
* @param cancelable
|
|
115
|
* 是否可取消
|
|
116
|
* @param cancelText
|
|
117
|
* 取消按钮文本
|
|
118
|
* @param confirmText
|
|
119
|
* 确认按钮文本
|
|
120
|
* @param isCancel
|
|
121
|
* 是否显示取消按钮
|
|
122
|
* @param cancelEvent
|
|
123
|
* 取消按钮点击事件
|
|
124
|
* @param confirmEvent
|
|
125
|
* 确认按钮点击事件
|
|
126
|
* @throws Exception
|
|
127
|
*/
|
|
128
|
public void confirm(String content, String title, boolean cancelable,
|
|
129
|
String cancelText, String confirmText, boolean isCancel,
|
|
130
|
final String cancelEvent, final String confirmEvent)
|
|
131
|
throws Exception {
|
|
132
|
final SweetAlertDialog dialog;
|
|
133
|
if (cancelText != null && isCancel != false) {
|
|
134
|
dialog = new SweetAlertDialog(context, WARNING_TYPE)
|
|
135
|
.setTitleText(title)
|
|
136
|
.setContentText(content)
|
|
137
|
.setCancelText(cancelText)
|
|
138
|
.setConfirmText(confirmText)
|
|
139
|
.showCancelButton(isCancel)
|
|
140
|
.setCancelClickListener(
|
|
141
|
new SweetAlertDialog.OnSweetClickListener() {
|
|
142
|
@Override
|
|
143
|
public void onClick(SweetAlertDialog sdialog) {
|
|
144
|
if (cancelEvent != null) {
|
|
145
|
sdialog.dismiss();
|
|
146
|
MobileUIWithSAD.this
|
|
147
|
.executeJs("WadeMobile.customEvents."
|
|
148
|
+ cancelEvent + "();");
|
|
149
|
}
|
|
150
|
}
|
|
151
|
})
|
|
152
|
.setConfirmClickListener(
|
|
153
|
new SweetAlertDialog.OnSweetClickListener() {
|
|
154
|
@Override
|
|
155
|
public void onClick(SweetAlertDialog sdialog) {
|
|
156
|
if (confirmEvent != null) {
|
|
157
|
sdialog.dismiss();
|
|
158
|
MobileUIWithSAD.this
|
|
159
|
.executeJs("WadeMobile.customEvents."
|
|
160
|
+ confirmEvent + "();");
|
|
161
|
}
|
|
162
|
}
|
|
163
|
});
|
|
164
|
} else {
|
|
165
|
dialog = new SweetAlertDialog(context, WARNING_TYPE)
|
|
166
|
.setTitleText(title)
|
|
167
|
.setContentText(content)
|
|
168
|
.setConfirmText(confirmText)
|
|
169
|
.showCancelButton(isCancel)
|
|
170
|
.setConfirmClickListener(
|
|
171
|
new SweetAlertDialog.OnSweetClickListener() {
|
|
172
|
@Override
|
|
173
|
public void onClick(SweetAlertDialog sdialog) {
|
|
174
|
if (confirmEvent != null) {
|
|
175
|
sdialog.dismiss();
|
|
176
|
MobileUIWithSAD.this
|
|
177
|
.executeJs("WadeMobile.customEvents."
|
|
178
|
+ confirmEvent + "();");
|
|
179
|
}
|
|
180
|
}
|
|
181
|
});
|
|
182
|
}
|
|
183
|
dialog.setCancelable(cancelable);
|
|
184
|
dialog.show();
|
|
185
|
}
|
|
186
|
/**
|
|
187
|
* js传参数,调用原生方法
|
|
188
|
*
|
|
189
|
* @param param
|
|
190
|
* @throws Exception
|
|
191
|
*/
|
|
192
|
public void loading(JSONArray param) throws Exception {
|
|
193
|
JSONObject jsonObject = param.getJSONObject(0);
|
|
194
|
JSONObject map = jsonObject.getJSONObject("map");
|
|
195
|
String title = map.getString("title");
|
|
196
|
Boolean cancelable = map.getBoolean("cancelable");
|
|
197
|
Long millisInFuture = map.getLong("millisInFuture");
|
|
198
|
Long countDownInterval = map.getLong("countDownInterval");
|
|
199
|
String nextEvent = map.getString("nextEvent");
|
|
200
|
Boolean hasNext = map.getBoolean("hasNext");
|
|
201
|
loading(title, cancelable, millisInFuture, countDownInterval, hasNext,
|
|
202
|
nextEvent);
|
|
203
|
}
|
|
204
|
/**
|
|
205
|
* 进度条提示框
|
|
206
|
*
|
|
207
|
* @param title
|
|
208
|
* 标题
|
|
209
|
* @param cancelable
|
|
210
|
* 是否可取消
|
|
211
|
* @param millisInFuture
|
|
212
|
* 倒计时总时间(毫秒)
|
|
213
|
* @param countDownInterval
|
|
214
|
* 间隔时间(毫秒)
|
|
215
|
* @param hasNext
|
|
216
|
* 是否继续弹出对话框
|
|
217
|
* @param nextEvent
|
|
218
|
* 对话框事件(JS定义弹出新对话框)
|
|
219
|
* @throws Exception
|
|
220
|
*/
|
|
221
|
public void loading(String title, boolean cancelable, long millisInFuture,
|
|
222
|
long countDownInterval, final boolean hasNext,
|
|
223
|
final String nextEvent) throws Exception {
|
|
224
|
final SweetAlertDialog pDialog = new SweetAlertDialog(context,
|
|
225
|
PROGRESS_TYPE).setTitleText(title);
|
|
226
|
pDialog.setCancelable(cancelable);
|
|
227
|
pDialog.show();
|
|
228
|
new CountDownTimer(millisInFuture, countDownInterval) {
|
|
229
|
public void onTick(long millisUntilFinished) {
|
|
230
|
// you can change the progress bar color by ProgressHelper every
|
|
231
|
// countDownInterval millis
|
|
232
|
i++;
|
|
233
|
switch (i) {
|
|
234
|
case 0:
|
|
235
|
pDialog.getProgressHelper().setBarColor(blue_btn_bg_color);
|
|
236
|
break;
|
|
237
|
case 1:
|
|
238
|
pDialog.getProgressHelper().setBarColor(
|
|
239
|
material_deep_teal_50);
|
|
240
|
break;
|
|
241
|
case 2:
|
|
242
|
pDialog.getProgressHelper().setBarColor(
|
|
243
|
success_stroke_color);
|
|
244
|
break;
|
|
245
|
case 3:
|
|
246
|
pDialog.getProgressHelper().setBarColor(
|
|
247
|
material_deep_teal_20);
|
|
248
|
break;
|
|
249
|
case 4:
|
|
250
|
pDialog.getProgressHelper().setBarColor(
|
|
251
|
material_blue_grey_80);
|
|
252
|
break;
|
|
253
|
case 5:
|
|
254
|
pDialog.getProgressHelper().setBarColor(
|
|
255
|
warning_stroke_color);
|
|
256
|
break;
|
|
257
|
case 6:
|
|
258
|
pDialog.getProgressHelper().setBarColor(
|
|
259
|
success_stroke_color);
|
|
260
|
break;
|
|
261
|
}
|
|
262
|
}
|
|
263
|
public void onFinish() {
|
|
264
|
i = -1;
|
|
265
|
pDialog.dismiss();
|
|
266
|
if (hasNext && nextEvent != null) {
|
|
267
|
MobileUIWithSAD.this.executeJs("WadeMobile.customEvents."
|
|
268
|
+ nextEvent + "();");
|
|
269
|
}
|
|
270
|
}
|
|
271
|
}.start();
|
|
272
|
}
|
|
273
|
}
|