悬浮窗教程,很简单。知识点不多,写下来 总结一下。
权限
首先 是在AndroidManiFest.xml 定义权限
如果 Android版本大于6.0 ,还需要引导用户 同意这个权限才行。并不是 你定义了就会给你的。
//判断是否 有悬浮窗的权限
if (!Settings.canDrawOverlays(getApplicationContext())) {
Intent alertOver = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
//这个页面不会返回值 所以用这个
startActivity(alertOver);
}
创建悬浮窗
下面这段代码 ,你可以直接复制到你的项目里面 运行。记得要申请权限!!!!
@SuppressLint("ClickableViewAccessibility")
void initShow() {
//获取服务
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// 设定参数
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
//Android 8 对悬浮窗 进行了 改变
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
//设置位图格式 默认是不透明的
layoutParams.format = PixelFormat.TRANSPARENT;
//设置宽 高 可以是MATCH_PARENT ,WRAP_CONTENT ,或者 确切的数值
layoutParams.width =300;
layoutParams.height = 300;
//设置悬浮窗 位置,这个受layoutParams.gravity 影响,它提供了从给定边缘的偏移量。
// 也就是说 这个悬浮窗的实际x,y位置。是这里x,y 加上偏移量后的。
layoutParams.x = 300;
layoutParams.y = 300;
//View以外的区域可以响应点击和触摸事件
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//容器与小部件之间的水平边距,作为容器宽度的百分比。
// 通俗的讲 ,就是悬浮窗会在 x,y位置的那个方向,影响 layoutParams.x ,layoutParams.y的偏移量
//这个默认是AXIS_X_SHIFT NO_GRAVITY
//将悬浮窗原点坐标系与屏幕重合 就是 把悬浮窗变成和普通view一样 都是以左上角为原点
//这个很重要
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
//创建view 就是悬浮窗里面的内容
TextView textView = new android.support.v7.widget.AppCompatTextView(getApplicationContext()){
@Override
public boolean performClick() {
return super.performClick();
}
};
textView.setText("我是悬浮窗");
//给view添加触摸事件,来使用悬浮窗可以移动
textView.setOnTouchListener(new View.OnTouchListener() {
//这个用于保存 当手指按下时候,离悬浮窗左上角的 x,y距离,这里设定初始值 是悬浮窗宽高的一半
float inX =layoutParams.width>>1, inY = layoutParams.height>>1;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//得到 点击位置 在悬浮窗中的位置
inX = event.getX();
inY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
//得到当前滑动位置,在整个屏幕上面的坐标
float nowX = event.getRawX();
float nowY = event.getRawY();
Zprint.log(this.getClass()," inX inY",inX,inY,nowX,nowY);
//屏幕X坐标 减去 点击时候 在悬浮窗中的位置 X 距离
layoutParams.x= (int) (nowX-inX);
//屏幕高度坐标 要注意 状态栏高度
layoutParams.y = (int) (nowY-statusHeight-inY);
//更新悬浮窗 位置
windowManager.updateViewLayout(textView, layoutParams);
break;
}
return false;
}
});
//这下面代码只是说明 在悬浮窗里面的view 与在activity里面的view ,没有任何不同
textView.setOnClickListener(v -> textView.setText("点击了"));
textView.setBackgroundColor(getColor(android.R.color.darker_gray));
//添加view
windowManager.addView(textView, layoutParams);
}
首先,是获得windowManager,设置activity的宽高 也是通过这个类来实现的。获取这个实例的方式是通过getSystemService(WINDOW_SERVICE)。在activity中的 也可以通过getWindowManager()来获取。
接着,就new 一个WindowManager.LayoutParams 布局参数。这个主要设置 悬浮窗的大小,类型(注意Android8),位置,位图之类的。上面 解释很清楚。如果不懂的 评论。
其次,就可以创建一个View 填充悬浮窗的内容。这个View既可以动态生成,也可以用xml写好,再用 LayoutInflater 实例出来。这个View和在activity中的view 并没有什么区别!!!
最后 调用添加view就可以了。
更新悬浮窗位置
使用windowManager.updateViewLayout(View, ViewGroup.LayoutParams);来更新悬浮窗的位置。我们可以给悬浮窗里面的view 添加触摸事件 ,来获取触摸位置,更新悬浮窗,我上面那段触摸事件 可以实现当你按住悬浮窗任意一个位置时候,拖动悬浮窗,离开悬浮窗时候,手指最后离开悬浮窗中的位置是和一开始按住悬浮窗中的位置 是一样的。
要注意 状态栏高度,ViewGroup.LayoutParams的x,y坐标是以状态栏和屏幕左边的交点为原点的。而getRaxY() 得到的是距离屏幕左上角的x,y坐标。
获取状态栏高度:
public int getStatusBarHeight() {
//局部变量 声明后,要赋值,不像成员变量会有默认值
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
更新悬浮窗内容
更新悬浮窗内容,就和在activity中更新view一样。找到view的实例进行更新view即可。记住要在主线程中
附 得到Android主线程
//handler 持有主线程的looper
private Handler mainHandler = new Handler(getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//这里接收处理mainHandler 发送来的message
//在这里写实际在主线程中运行的代码
return false;
}
});
//在你要更新view的地方,或者需要主线程操作的地方 发送message
mainHandler.sendMessage(Message);
如果 你感觉上面代码 还是繁琐,也可以调用handler的post(Runnable)方法
mainHandler.post(Runnable r)