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
}

+ 39 - 0
ipu-pathmenu/src/com/ai/ipu/ipu_pathmenu/PathMenuActivity.java

@ -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
}

+ 495 - 0
ipu-pathmenu/src/com/ai/ipu/ipu_pathmenu/PathMenuLayout.java

@ -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
}

+ 94 - 0
ipu-pathmenu/src/com/ai/ipu/ipu_pathmenu/PathMenuService.java

@ -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
}

+ 123 - 0
ipu-pathmenu/src/com/ai/ipu/ipu_pathmenu/RotateAndTranslateAnimation.java

@ -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
}

BIN
wade-mobile-common/libs/ipu-mobile-1.0.jar


BIN
wade-mobile-common/res/drawable/composer_button.png


BIN
wade-mobile-common/res/drawable/composer_camera.png


BIN
wade-mobile-common/res/drawable/composer_close.png


BIN
wade-mobile-common/res/drawable/composer_icn_plus.png


BIN
wade-mobile-common/res/drawable/composer_music.png


BIN
wade-mobile-common/res/drawable/composer_place.png


BIN
wade-mobile-common/res/drawable/composer_sleep.png


BIN
wade-mobile-common/res/drawable/composer_thought.png


BIN
wade-mobile-common/res/drawable/composer_with.png


+ 28 - 0
wade-mobile-common/res/layout/activity_float.xml

@ -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>

+ 29 - 0
wade-mobile-common/res/layout/float_menu.xml

@ -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>

+ 0 - 78
wade-mobile-func/src/com/wade/mobile/func/IpuWindowWebView.java

@ -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
}

+ 52 - 0
wade-mobile-func/src/com/wade/mobile/func/MobilePathMenu.java

@ -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
}

+ 13 - 39
wade-mobile-func/src/com/wade/mobile/func/MobileUI.java

@ -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

+ 273 - 0
wade-mobile-func/src/com/wade/mobile/func/MobileUIWithSAD.java

@ -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
}

excel - Nuosi Git Service

表格收集器

wangkang3 961c80a467 初始化 5 years ago
src 961c80a467 初始化 5 years ago
.gitignore 961c80a467 初始化 5 years ago
pom.xml 961c80a467 初始化 5 years ago