android 悬浮窗口响应事件,Android悬浮窗及其拖动事件

主页面布局很简单,只有一个RelativelyLayout

悬浮窗中只有一个TextView

主界面代码

public class MainActivity extends AppCompatActivity {

private static final String TAG = "MainActivity";

/** * 获取sdk版本号 */

private static final int SDKVERSION = Build.VERSION.SDK_INT;

/** * 窗口管理器 */

private WindowManager windowManager;

/** * 浮动按钮布局 */

private View floatingButtonView;

/** * 浮动按钮布局参数 */

private WindowManager.LayoutParams floatingButtonParams;

/** * 顶部状态栏高度 */

private int top;

/** * 浮动窗原始位置 */

private float startPositionX = 0;

private float startPositionY = 0;

/** * 屏幕宽高 */

private int contentWidth;

private int contentHeight;

private float lastX;

private float lastY;

private float mTouchStartX;

private float mTouchStartY;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

initFloatingButton();

}

private void initFloatingButton() {

//浮动按钮布局

floatingButtonView = LayoutInflater.from(this).inflate(R.layout.floating_view, null);

floatingButtonParams = new WindowManager.LayoutParams();

if (SDKVERSION >= 19) {

floatingButtonParams.type = WindowManager.LayoutParams.TYPE_TOAST;

} else {

floatingButtonParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;

}

floatingButtonParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams

.FLAG_NOT_FOCUSABLE;

floatingButtonParams.format = PixelFormat.TRANSLUCENT;

floatingButtonParams.width = WindowManager.LayoutParams.WRAP_CONTENT;

floatingButtonParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

floatingButtonParams.gravity = Gravity.TOP| Gravity.LEFT;

floatingButtonParams.x = 0;

floatingButtonParams.y = 0;

Log.d(TAG, "initFloatingButton: " + floatingButtonParams.x + " " + floatingButtonParams.y);

windowManager.addView(floatingButtonView, floatingButtonParams);

floatingButtonView.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

lastX = event.getRawX();

lastY = event.getRawY() - top;

switch (event.getActionMasked()) {

case MotionEvent.ACTION_DOWN:

mTouchStartX = event.getX();

mTouchStartY = event.getY();

//记录悬浮窗原始位置

startPositionX = floatingButtonParams.x;

startPositionY = floatingButtonParams.y;

Log.d(TAG, "onTouch down : m " + mTouchStartX + " " + mTouchStartY);

Log.d(TAG, "onTouch down : last " + lastX + " " + lastY);

Log.d(TAG, "onTouch down : start " + startPositionX + " " + startPositionY);

Log.d(TAG, "onTouch down : Params " + floatingButtonParams.x + " " + floatingButtonParams

.y);

break;

case MotionEvent.ACTION_MOVE:

//计算新的位置

floatingButtonParams.x = (int) (lastX - mTouchStartX);

floatingButtonParams.y = (int) (lastY - mTouchStartY);

//如果原始位置在中间,所以需要减去屏幕宽高的一半

// floatingButtonParams.x = (int) (lastX - mTouchStartX - contentWidth / 2);

// floatingButtonParams.y = (int) (lastY - mTouchStartY - contentHeight / 2);

Log.d(TAG, "onTouch move : m " + mTouchStartX + " " + mTouchStartY);

Log.d(TAG, "onTouch move : last " + lastX + " " + lastY);

Log.d(TAG, "onTouch move : start " + startPositionX + " " + startPositionY);

Log.d(TAG, "onTouch move : Params " + floatingButtonParams.x + " " + floatingButtonParams

.y);

//

windowManager.updateViewLayout(floatingButtonView, floatingButtonParams);

break;

case MotionEvent.ACTION_UP:

Log.d(TAG, "onTouch up : m " + mTouchStartX + " " + mTouchStartY);

Log.d(TAG, "onTouch up : last " + lastX + " " + lastY);

Log.d(TAG, "onTouch up : start " + startPositionX + " " + startPositionY);

Log.d(TAG, "onTouch up : Params " + floatingButtonParams.x + " " + floatingButtonParams.y);

if (Math.abs(floatingButtonParams.x - startPositionX) < 20 && Math.abs(floatingButtonParams.y

- startPositionY) < 20) {

Toast.makeText(MainActivity.this, "click", Toast.LENGTH_LONG);

}

break;

}

return false;

}

});

}

@Override

public void onWindowFocusChanged(boolean hasFocus) {

super.onWindowFocusChanged(hasFocus);

//获取整个布局的宽高

Point size = new Point();

windowManager.getDefaultDisplay().getSize(size);

contentWidth = size.x;

contentHeight = size.y;

//下面这两个方法已经不建议使用了

// windowManager.getDefaultDisplay().getWidth();

// windowManager.getDefaultDisplay().getHeight();

Rect rect = new Rect();

// /取得整个视图部分,注意,如果你要设置标题样式,这个必须出现在标题样式之后,否则会出错

getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);

top = rect.top;//状态栏的高度,所以rect.height,rect.width分别是系统的高度的宽度

Log.d(TAG, "onWindowFocusChanged: " + contentWidth + " " + contentHeight + " " + top);

}

}

但是上述代码有点问题,如果将悬浮窗的初始位置设为Gravity.CENTER,在拖动的最开始会有个抖动。如果将主题设为没有title的主题,然后出去界面的宽高,在拖动的时候减去宽高的一半,则没有抖动。可是加上有title的主题后,会有微小的抖动。这一bug暂未修复。

为什么悬浮窗的属性要设置成TYPE_TOAST,请看这里

1、使用 type 值为 WindowManager.LayoutParams.TYPE_PHONE 和 WindowManager.LayoutParams.TYPE_SYSTEM_ALERT 需要申请 android.permission.SYSTEM_ALERT_WINDOW 权限,否则无法显示,报错:

E/AndroidRuntime: android.view.WindowManagerBadTokenException:Unabletoaddwindowandroid.view.ViewRootW@b64b5458 – permission denied for this window type

2、type 值为 WindowManager.LayoutParams.TYPE_TOAST 显示的 System overlay view 不需要权限,即可在任何平台显示。

但仅在 API level >= 19 时可以达到目的。API level 19 以下因无法接收无法接收触摸(点击)和按键事件,故无法达到目的。

3、对于 API level < 19 的机器(MIUI除外),想要达到目的,需要:

要有 android.permission.SYSTEM_ALERT_WINDOW 权限

将 type 设置为 WindowManager.LayoutParams.TYPE_PHONE 或者 WindowManager.LayoutParams.TYPE_SYSTEM_ALERT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值