悬浮窗 适配
完美的悬浮窗
支持滑动 双击放大逻辑
public class Test extends Activity {
private static WindowManager mWindowManager;
private WindowManager.LayoutParams wmParams;
private int width, height;
private FrameLayout mFloatLayout;
private int screenWidth = ScreenUtils.getScreenWidth(this);
private int screenHeight = ScreenUtils.getScreenHeight(this);
private int localWidth = screenWidth / 4;
private int localHeight = localWidth * 16 / 9;
private boolean isBig = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initFloatWindow();
}
private void initFloatWindow() {
wmParams = new WindowManager.LayoutParams();
mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
// 获取屏幕的高度
DisplayMetrics dm = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(dm);
width = dm.widthPixels;
height = dm.heightPixels;
// 设置window type
if (Build.VERSION.SDK_INT >= 26) { // 8.0新特性
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
// 设置图片格式,效果为背景透明
wmParams.format = PixelFormat.RGBA_8888;
// 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams
.FLAG_NOT_TOUCH_MODAL;
// 调整悬浮窗显示的停靠位置为右侧侧置顶,方便实现触摸滑动
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
// 以屏幕左上角为原点,设置x、y初始值
wmParams.x = width;
wmParams.y = height * 2 / 3; // ScreenUtils.dp2px(this, 40);
// 设置悬浮窗口长宽数据
wmParams.width = localWidth;
wmParams.height = localHeight;
LayoutInflater inflater = LayoutInflater.from(getApplication());
// 获取浮动窗口视图所在布局
mFloatLayout = (FrameLayout) inflater.inflate(R.layout.float_window_layout, null);
TextView vTime = new TextView(this);
vTime.setText("ABCD");
mFloatLayout.addView(vTime);
initClick();
setDoubleClick();
}
// 设置监听浮动窗口的触摸移动
private void initClick() {
mFloatLayout.setOnTouchListener(new View.OnTouchListener() {
float dx, dy, mx, my;
float moveX, moveY;
// 这里用于up和move不冲突
boolean isMove;
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
// getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记住手按下的位置
dx = event.getRawX();
dy = event.getRawY();
// mx = v.getWidth()/2;
// my = v.getHeight()/2;
// 计算手相对控件本身按下的位置
mx = event.getX();
my = event.getY();
isMove = false; // 这里需要设置默认值为false,避免up部分出bug
return false;
case MotionEvent.ACTION_MOVE:
// 计算手移动的距离
int x = Math.abs((int) (event.getRawX() - dx));
int y = Math.abs((int) (event.getRawY() - dy));
// 如果x和y距离都小于5,说明用户并没打算移动,只是手触摸时产生的move
if (x < 5 || y < 5) {
isMove = false;
return false;
} else {
isMove = true;
}
// 计算控件移动的距离
x = (int) (event.getRawX() - mx);
y = (int) (event.getRawY() - my);
wmParams.x = x;
// 25为状态栏的高度
wmParams.y = y;
// 刷新
mWindowManager.updateViewLayout(mFloatLayout, wmParams);
// return false;
return true;
case MotionEvent.ACTION_UP:
// 这里要注意边界问题
float finalX = event.getRawX();
float finalY = event.getRawY();
// 控制上边距
if (finalY < v.getHeight()) {
moveX = 0;
moveY = finalX - my;
}
// 下边距
if (finalY > height - v.getHeight()) {
moveX = finalX - mx;
moveY = finalY - v.getHeight();
}
// 判断控件改停留在左边距还是右边距
if (finalX - v.getWidth() / 2 < width / 2) {
moveX = 0;
moveY = finalY - my;
} else if (finalX - v.getWidth() / 2 > width / 2) {
moveX = width - v.getWidth();
moveY = finalY - my;
}
wmParams.x = (int) moveX;
wmParams.y = (int) moveY;
if (isMove) {
mWindowManager.updateViewLayout(mFloatLayout, wmParams);
}
// return isMove; // false为down,true为move
return false;
default:
break;
}
// 这里return ture,说明本次事件已经被处理,不会传给父亲
return false;
}
});
}
private void setDoubleClick() {
mFloatLayout.setOnClickListener(new DoubleClickListener() {
@Override
public void onMultiClick(View v) {
// TODO: 2019/1/10 change state
ToastUtil.toastNormal("change state");
if (isBig) {
wmParams.width = localWidth;
wmParams.height = localHeight;
isBig = false;
mWindowManager.updateViewLayout(mFloatLayout, wmParams);
} else {
isBig = true;
wmParams.width = screenWidth;
wmParams.height = screenHeight;
mWindowManager.updateViewLayout(mFloatLayout, wmParams);
}
}
});
}
}
public abstract class DoubleClickListener implements View.OnClickListener {
// 两次点击按钮之间的点击间隔
private static final int MIN_CLICK_DELAY_TIME = 500;
private static long lastClickTime;
public abstract void onMultiClick(View v);
@Override
public void onClick(View v) {
long curClickTime = System.currentTimeMillis();
if ((curClickTime - lastClickTime) <= MIN_CLICK_DELAY_TIME) {
onMultiClick(v);
} else {
lastClickTime = curClickTime;
}
}
}
public class ScreenUtils {
/**
* 获取屏幕高度(px)
*/
public static int getScreenHeight(Context context) {
return context.getResources().getDisplayMetrics().heightPixels;
}
/**
* 获取屏幕宽度(px)
*/
public static int getScreenWidth(Context context) {
return context.getResources().getDisplayMetrics().widthPixels;
}