背景
我们知道PopupWindow可以实现弹窗,但是如果要实现系统弹窗(即悬浮在所有view、dialog之上),需要调用通过反射PopupWindow类的私有方法setWindowLayoutType()。但如果某个手机厂商的修改了Framework层,这种反射调用就失败了,因此如果要实现系统弹窗,需要另辟蹊径。
解决方法
解决方法就是使用WindowManager,及用来设置参数的WindowManager.LayoutParams
WindowManager mWindowManager = getWindowManager();
WindowManager.LayoutParams mWindowLayoutParams = new WindowManager.LayoutParams();
加载弹窗根布局
mWindowView = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.window_layout, null);
设置弹窗位置
mWindowLayoutParams.gravity = Gravity.START;
mWindowLayoutParams.x = 0;
mWindowLayoutParams.y = 0;
设置弹窗类型(系统浮窗)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
设置触摸事件穿透
mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWindowView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int[] popupLocation = new int[2];
mWindowView.getLocationOnScreen(popupLocation);
event.offsetLocation(popupLocation[0], popupLocation[1]);
MainActivity.this.dispatchTouchEvent(event);
return false;
}
});
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL表示允许该弹窗外面的触摸事件发送到其下面的窗口,否则所有触摸事件都会发送到当前的弹窗窗口; WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE表示弹窗不获取焦点,因此该弹窗不会接收任何按钮或按键事件,对应的事件会传到其下面的窗口。 mWindowView是弹窗的根布局,当触摸事件从弹窗传到下面的activity时,需要进行坐标转换。
设置弹窗半透明
mWindowLayoutParams.format = PixelFormat.TRANSLUCENT;
设置弹窗尺寸
mWindowLayoutParams.height = 1800;
mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
设置软键盘适配
mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
添加或移除view
@Override
protected void onStop() {
super.onStop();
mWindowManager.removeView(mWindowView);
}
@Override
protected void onResume() {
super.onResume();
mWindowManager.addView(mWindowView, mWindowLayoutParams);
}
源码地址
参见gitlab仓库
|