觉得魅族的悬浮球做的很不错,主要是简洁方便。于是就自己实现了一下。项目源代码:FloatingBall源代码
主要是解决三个方面的问题:
1.桌面悬浮窗的实现
2.设置悬浮球触摸事件(拖动,点击,上下左右滑动)
2.模拟虚拟按键(返回,home,任务面板,电源键,菜单键等)
(一)桌面悬浮窗的实现
通过WindowManager将自定义的悬浮球布局ballview添加到手机屏幕
//布局转化成要显示的view
ballView = LayoutInflater.from(this).inflate(R.layout.floatball, null);
wm = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
ballWmParams = new WindowManager.LayoutParams();
//设置悬浮窗的相关参数
ballWmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
ballWmParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
ballWmParams.gravity = Gravity.LEFT | Gravity.TOP;
ballWmParams.x = MainActivity.sp.getInt("ballWmParamsX",0);
ballWmParams.y = MainActivity.sp.getInt("ballWmParamsY",0);
ballWmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
ballWmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
ballWmParams.format = PixelFormat.RGBA_8888;
//将悬浮球view添加到屏幕
wm.addView(ballView, ballWmParams);
(二)悬浮球触摸事件
通过设置悬浮球buttton的setOnTouchListener监听触摸事件
1.拖动悬浮球改变位置
根据滑动的距离改变WindowManager的ballWmParams.x和ballWmParams.y实现悬浮球的拖动
2.点击和上下左右滑动
通过对滑动事件的x,y坐标量变化的分析,判断是点击,还是上下左右滑动
//注册触碰事件监听器
floatImage.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
x = event.getX();
y = event.getY();
if(tag == 0)
{
oldOffsetX = ballWmParams.x;
oldOffsetY = ballWmParams.y;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
ismoving = false;
mTouchStartX = (int)event.getX();
mTouchStartY = (int)event.getY();
break;
case MotionEvent.ACTION_MOVE:
ismoving = true;
tag = 1;
ballWmParams.x += (int) (x - mTouchStartX)/3;// 减小偏移量,防止过度抖动
ballWmParams.y += (int) (y - mTouchStartY)/3;// 减小偏移量,防止过度抖动
if(canmove)
{
updateViewPosition();
saveStates("ballWmParamsX", ballWmParams.x);
saveStates("ballWmParamsY",ballWmParams.y);
}
break;
case MotionEvent.ACTION_UP:
mTouchStartX = mTouchStartY = 0;
newOffsetX = ballWmParams.x;
newOffsetY = ballWmParams.y;
// 点击悬浮球
if (Math.abs(oldOffsetX - newOffsetX) <= 20 && Math.abs(oldOffsetY - newOffsetY) <= 20) {
onFloatBallClick();
onClearOffset();
}
if(!canmove)
{
//向上滑动
if ((oldOffsetY - newOffsetY) - Math.abs(oldOffsetX - newOffsetX) > 20 && (oldOffsetY - newOffsetY) >20 )
{
onFloatBallFlipUp();
}
//向下滑动
else if ((newOffsetY - oldOffsetY) - Math.abs(oldOffsetX - newOffsetX) > 20 && (newOffsetY - oldOffsetY) > 20 ){
onFloatBallFlipDown();
}
//向左滑动
else if ((oldOffsetX - newOffsetX) - Math.abs(oldOffsetY - newOffsetY) > 20 && (oldOffsetX - newOffsetX) > 20 )
{
onFloatBallFlipLeft();
}
//向右滑动
else if((newOffsetX - oldOffsetX) - Math.abs(oldOffsetY - newOffsetY) > 20 && (newOffsetX - oldOffsetX) > 20){
onFloatBallFlipRight();
}
onClearOffset();
}
tag = 0;
break;
}
//如果拖动则返回false,否则返回true
if(ismoving == false){
return false;
}else{
return true;
}
}
});
采用adb shell的input实现,反应速度慢,有延时,效果不好。
所以采用IWindowManager和InputManager实现模拟按键事件。
public static void simulateKey(int keyCode) {
//使用KeyEvent模拟按键按下与弹起
long l = SystemClock.uptimeMillis();
KeyEvent localKeyEvent = new KeyEvent(l,l,KeyEvent.ACTION_DOWN,keyCode,0);
KeyEvent localKeyEvent1 = new KeyEvent(l,l,KeyEvent.ACTION_UP,keyCode,0);
//判断sdk版本,老版本使用IWindowManager注入按键事件,新版本使用InputManager注入按键事件
//*******IWindowManager和InputManager都是隐藏类,必须在重新生成sdk中的android.jar,并包含两个类及其依赖*****
if (Build.VERSION.SDK_INT < 16)
{
try {
IWindowManager.Stub.asInterface(ServiceManager.getService("window")).injectKeyEvent(localKeyEvent, true);
IWindowManager.Stub.asInterface(ServiceManager.getService("window")).injectKeyEvent(localKeyEvent1, true);
return;
} catch (RemoteException e) {
e.printStackTrace();
return;
}
}
InputManager.getInstance().injectInputEvent(localKeyEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
InputManager.getInstance().injectInputEvent(localKeyEvent1, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}