天天看点

可移动悬浮窗关于随着手势移动的坐标区分与需要注意的事项

想要做一个简单的像悬浮球那样的能在手机桌面上随意拖动的效果,首先你需要知道 1:往手机桌面上添加一个自定义view(悬浮球)使用的是WindowManager.addView(View view, WindowManager.LayoutParams params); ---->WindowManager.LayoutParams是用于指定view在桌面中的位置,以及view在桌面中的一些属性; 2:如果想出现悬浮球效果,在清单文件中必须声明 <uses-permission android :name= "android.permission.SYSTEM_ALERT_WINDOW" /> 权限 3:如果想随意移动你的自定义View必须在View中重写onTouchEvent()方法来记录坐标的变化(注意,每个View中都有onTouchEvent方法,注意区别onTouchEvent方法),最后使用windowManager.updateViewLayout(View view, WIndowManager.LayoutParams params);更新自定义view(悬浮球)的位置

现在我们开始实现自定义view(悬浮球) 1:先定义一个悬浮球的布局文件small_floatball,由于这次讲述的重点不在这里,所以不详细说明,直接上代码 <? xml version= "1.0" encoding= "utf-8" ?> <FrameLayout xmlns: android = "http://schemas.android.com/apk/res/android" android :layout_width= "50dp" android :layout_height= "50dp" android :id= "@+id/small_window_layout" android :background= "@android:color/transparent" android :orientation= "vertical" >

<ImageView android :layout_width= "35dp" android :layout_height= "35dp" android :id= "@+id/img_bg" android :background= "@drawable/icon_bg" android :layout_gravity= "center" /> <ImageView android :layout_width= "wrap_content" android :layout_height= "wrap_content" android :id= "@+id/img_ball" android :background= "@drawable/icon_ball" android :layout_gravity= "center" /> </FrameLayout>

效果图:(注意xml文件中的Framelayout和ImageView的大小)

2:现在我们开始自定义view public class FloatBall extends LinearLayout {

可移动悬浮窗关于随着手势移动的坐标区分与需要注意的事项
可移动悬浮窗关于随着手势移动的坐标区分与需要注意的事项

private float mDownX; private float mDownY;

private float mOnMoveX; private float mOnMoveY;

private float mInViewX; private float mInViewY;

private WindowManager.LayoutParams mParams; private WindowManager mWindowManager;

public FloatBall(Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.small_floatball, this);

View view = findViewById(R.id.small_window_layout);

mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

mParams = new WindowManager.LayoutParams(); mParams.type = WindowManager.LayoutParams.TYPE_PHONE; mParams.format = PixelFormat.RGBA_8888; mParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mParams.gravity = Gravity.LEFT | Gravity.TOP; mParams.height = view.getLayoutParams().height; mParams.width = view.getLayoutParams().width; mParams.x = mWindowManager.getDefaultDisplay().getWidth(); mParams.y = mWindowManager.getDefaultDisplay().getHeight() / 2;

mWindowManager.addView(this, mParams); }

@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ //当按手指按下时的事件 case MotionEvent.ACTION_DOWN: mDownX = event.getRawX(); mDownY = event.getRawY() - getStatusBarHeight(); mOnMoveX = event.getRawX(); mOnMoveY = event.getRawY() - getStatusBarHeight(); mInViewX = event.getX(); mInViewY = event.getY(); break; //当手指移动时的事件 case MotionEvent.ACTION_MOVE: mOnMoveX = event.getRawX(); mOnMoveY = event.getRawY() - getStatusBarHeight(); upDateFloatBallLocation(); break; //当手指拿起时的事件 case MotionEvent.ACTION_UP: // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。 if (mOnMoveX == mDownX && mOnMoveY == mDownY) {

} break;

} return true; }

private void upDateFloatBallLocation() { mParams.x = (int)(mOnMoveX - mInViewX); mParams.y = (int)(mOnMoveY - mInViewY); mWindowManager.updateViewLayout(this, mParams); }

//这个方法是获得状态栏的高度,可以不用深究 private int getStatusBarHeight() { int statusBarHeight = 0; if (statusBarHeight == 0) { try { Class<?> c = Class.forName("com.android.internal.R$dimen"); Object o = c.newInstance(); Field field = c.getField("status_bar_height"); int x = (Integer) field.get(o); statusBarHeight = getResources().getDimensionPixelSize(x); } catch (Exception e) { e.printStackTrace(); } } return statusBarHeight; } }

接下来我们来说刚才没有解释的三个方法和mParams参数的含义

1:构造函数中的mParams的主要的参数含义

//初始化mParams mParams = new WindowManager.LayoutParams();

mParams.type = WindowManager.LayoutParams.TYPE_PHONE; mParams.format = PixelFormat.RGBA_8888; mParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

//这个gravity属性是指定view的对齐方式,之前我们在位置坐标中所得左上角就是在这里定义的 mParams.gravity = Gravity.LEFT | Gravity.TOP; //这个是设置view在桌面中的大小 mParams.height = view.getLayoutParams().height; mParams.width = view.getLayoutParams().width; //这个非常非常容易搞错!!!!这个是定义控件在桌面中的位置,这个位置是以view的左上角为原点的,而不是以这个view的中心作为原点!搞清楚这个你就可以理解之后的onTouchEvent中的一些不理解的地方 mParams.x = mWindowManager.getDefaultDisplay().getWidth();//让这个悬浮按钮的初始x坐标是最左边 mParams.y = mWindowManager.getDefaultDisplay().getHeight() / 2; //让这个悬浮按钮的初始y坐标是中间

2:onTouchEvent() 还记得之前的那几个坐标参数的含义吗

private float mDownX; private float mDownY;

private float mOnMoveX; private float mOnMoveY;

private float mInViewX; private float mInViewY;

public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ //当按手指按下时的事件 case MotionEvent.ACTION_DOWN: mDownX = event.getRawX(); mDownY = event.getRawY() - getStatusBarHeight(); mOnMoveX = event.getRawX(); mOnMoveY = event.getRawY() - getStatusBarHeight(); mInViewX = event.getX(); mInViewY = event.getY(); break; //当手指移动时的事件 case MotionEvent.ACTION_MOVE: mOnMoveX = event.getRawX(); mOnMoveY = event.getRawY() - getStatusBarHeight(); upDateFloatBallLocation(); break; //当手指拿起时的事件 case MotionEvent.ACTION_UP: // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。 if (mOnMoveX == mDownX && mOnMoveY == mDownY) {

} break;

} return true; }

在这里我只想解释一下为什么要剪掉状态栏的高度,因为悬浮窗是不能移动到状态栏之上的,但是我们的getRawY的参考坐标原点是屏幕左上角,也是状态栏的左上角,因为移动不过去我们就把坐标原点改为ActionBar的左上角,所以剪掉了状态栏的高度

2:upDateFloatBallLocation()

private void upDateFloatBallLocation() { mParams.x = (int)(mOnMoveX - mInViewX); mParams.y = (int)(mOnMoveY - mInViewY); //更新悬浮球位置函数 mWindowManager.updateViewLayout(this, mParams); }

这里我解释下 mParams.x = (int)(mOnMoveX - mInViewX); mParams.y = (int)(mOnMoveY - mInViewY); 这里回想刚才1中说明的那个问题,mParams.x和mParams.y是自定义view的位置,但是这个位置是以这个view的左上角的点作为原点的,如果mInViewX和mInViewY就是(0, 0),也就是view的左上角,那么mParams.x = mOnMoveX; mParams.y=mOnMoveY; 但是如果不是(0, 0)那么你想想是不是应该是 mParams.x = (int)(mOnMoveX - mInViewX); mParams.y = (int)(mOnMoveY - mInViewY); 如果想不通,那就画图试试,注意getX, getRawX, getY和getRawY的区别

这里提醒下,mParams必须是WindowManager.LayoutParams而不是view的LayoutParams

源码地址:http://download.csdn.net/detail/szx584820/9808229