Android悬浮窗开启 适配所有机型(附源码)

1.开启悬浮窗权限

清单文件中添加:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

2.检测是否开启了悬浮窗权限

    public static boolean canDrawOverlays(Context context) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;else if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {return Settings.canDrawOverlays(context);} else {if (Settings.canDrawOverlays(context)) return true;try {WindowManager mgr = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);if (mgr == null) return false; //getSystemService might return nullView viewToAdd = new View(context);WindowManager.LayoutParams params = new WindowManager.LayoutParams(0, 0, android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);viewToAdd.setLayoutParams(params);mgr.addView(viewToAdd, params);mgr.removeView(viewToAdd);return true;} catch (Exception e) {e.printStackTrace();}return false;}}

3.Activity调用

if (canDrawOverlays(getApplicationContext())) {myWindowManager = MyWindowManager.getInstance();myWindowManager.createNormalView(getApplicationContext(), this);} else {requestSettingCanDrawOverlays();}

4.开启悬浮框

    private void requestSettingCanDrawOverlays() {int sdkInt = Build.VERSION.SDK_INT;if (sdkInt >= Build.VERSION_CODES.O) {//8.0以上Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);startActivityForResult(intent, OPEN_OVERLAY_PERMISSION);} else if (sdkInt >= Build.VERSION_CODES.M) {//6.0-8.0Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + getPackageName()));startActivityForResult(intent, OPEN_OVERLAY_PERMISSION);} else {//4.4-6.0以下//无需处理了}}

5.Activity开启悬浮框回调

    @Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);//浮窗权限申请if (requestCode == OPEN_OVERLAY_PERMISSION) {new CountDownTimer(1000, 100) {@Overridepublic void onTick(long millisUntilFinished) {}@Overridepublic void onFinish() {if (DeviceTools.canDrawOverlays(getApplicationContext())) {myWindowManager = MyWindowManager.getInstance();myWindowManager.createNormalView(getApplicationContext(), MainActivity.this);} else {Toast.makeText(MainActivity.this, "您拒绝了此权限,将无法显示悬浮窗", Toast.LENGTH_SHORT).show();}}}.start();}}

6.MyWindowManager代码

/*** 悬浮窗管理器* 创建,移除* 单例模式*/
public class MyWindowManager {private FloatNormalView normalView;private static MyWindowManager instance;private WindowManager windowManager;private MyWindowManager() {}public static MyWindowManager getInstance() {if (instance == null)instance = new MyWindowManager();return instance;}private WindowManager getWindowManager(Context context) {if (windowManager == null)windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);return windowManager;}public void setContent(String talkResult){if (normalView != null) {normalView.setResultContent(talkResult);}}/*** 创建小型悬浮窗*/public Activity activity = null;public void createNormalView(Context context, Activity activity) {if (normalView == null) {normalView = new FloatNormalView(context,activity);}this.getWindowManager(context);this.activity = activity;}/*** 移除悬浮窗** @param context*/public void removeNormalView(Context context) {if (normalView != null) {windowManager.removeView(normalView);normalView = null;}}}

7.FloatNormalView代码

/*** 悬浮窗*/
public class FloatNormalView extends LinearLayout {/*** 记录小悬浮窗的宽度*/public static int viewWidth;/*** 记录小悬浮窗的高度*/public static int viewHeight;/*** 记录系统状态栏的高度*/private static int statusBarHeight;/*** 用于更新小悬浮窗的位置*/private WindowManager windowManager;/*** 小悬浮窗的参数*/private WindowManager.LayoutParams mParams;/*** 记录当前手指位置在屏幕上的横坐标值*/private float xInScreen;/*** 记录当前手指位置在屏幕上的纵坐标值*/private float yInScreen;/*** 记录手指按下时在屏幕上的横坐标的值*/private float xDownInScreen;/*** 记录手指按下时在屏幕上的纵坐标的值*/private float yDownInScreen;/*** 记录手指按下时在小悬浮窗的View上的横坐标的值*/private float xInView;/*** 记录手指按下时在小悬浮窗的View上的纵坐标的值*/private float yInView;private Context mContext = null;private ActivityManager activityManager = null;private Activity mActivity = null;private TextView tvResult = null;public FloatNormalView(Context context, Activity activity) {super(context);mContext = context;mActivity = activity;windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ;LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);View view = findViewById(R.id.ll_float_normal);viewWidth = view.getLayoutParams().width;viewHeight = view.getLayoutParams().height;tvResult = view.findViewById(R.id.tv_result);initLayoutParams();}/*** 初始化参数*/private void initLayoutParams() {//屏幕宽高int screenWidth = windowManager.getDefaultDisplay().getWidth();int screenHeight = windowManager.getDefaultDisplay().getHeight();mParams = new WindowManager.LayoutParams();//总是出现在应用程序窗口之上。mParams.type = WindowManager.LayoutParams.TYPE_PHONE;// FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口// FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;//悬浮窗默认显示的位置mParams.gravity = Gravity.RIGHT | Gravity.TOP;//指定位置mParams.x = screenWidth - viewWidth * 2;mParams.y = screenHeight / 2 + viewHeight * 2;//悬浮窗的宽高mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;mParams.format = PixelFormat.TRANSPARENT;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 系统提示window}windowManager.addView(this, mParams);}public void setResultContent(String talkResult){tvResult.setText(talkResult);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度xInView = event.getX();yInView = event.getY();xDownInScreen = event.getRawX();yDownInScreen = event.getRawY() - getStatusBarHeight();xInScreen = event.getRawX();yInScreen = event.getRawY() - getStatusBarHeight();break;case MotionEvent.ACTION_MOVE:xInScreen = event.getRawX();yInScreen = event.getRawY() - getStatusBarHeight();// 手指移动的时候更新小悬浮窗的位置updateViewPosition();break;case MotionEvent.ACTION_UP:// 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {if (!DeviceTools.isRunForeground()){Log.e("app---","后台");
//                        activityManager.moveTaskToFront(mActivity.getTaskId(), ActivityManager.MOVE_TASK_WITH_HOME);EventBus.getDefault().post(new DVRConnectBean(Type.DeviceCodeStatus.UNKNOWN, EventBusTag.INSTALL_APP));}}break;default:break;}return true;}public boolean IsForeground(Context context) {ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);if (tasks != null && !tasks.isEmpty()) {ComponentName topActivity = tasks.get(0).topActivity;if (topActivity.getPackageName().equals(context.getPackageName())) {return true;}}return false;}/*** 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。** @param params 小悬浮窗的参数*/public void setParams(WindowManager.LayoutParams params) {mParams = params;}/*** 更新小悬浮窗在屏幕中的位置。*/private void updateViewPosition() {
//        mParams.x = (int) (xInScreen - xInView);mParams.y = (int) (yInScreen - yInView);windowManager.updateViewLayout(this, mParams);}/*** 用于获取状态栏的高度。** @return 返回状态栏高度的像素值。*/private int getStatusBarHeight() {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;}}

意见反馈

如有问题请及时联系我,及时修复问题

Android悬浮窗开启 适配所有机型(附源码)相关推荐

  1. 【android-tips】如何在android应用中插入百度广告(附源码)

    (转载请注明出处:http://blog.csdn.net/buptgshengod) 1.介绍    现在游戏中的广告基本上已经成为了游戏创作者的一个重要的收入来源.其实插入广告还是挺简单的,本文选 ...

  2. ANDROID物联网开发从入门到实战附源码

    本书从获取源码和搭建应用开发环境开始讲起,依次讲解了基础知识篇.数据传输篇.信息识别篇.传感器应用篇和技术提高篇这 5大部分内容. 目录 第1篇 基础知识篇 第1章 Android系统介绍 2 1.1 ...

  3. Android 商城类应用实战之购物车附源码

    本文授权发布公众号[刘桂林],星球[Hi Android] 本文为实战类项目,所以陈述的逻辑为实现流程 + 核心代码,主要实现的还是购物车的动画与结算,首先我们来看下整体的效果图: 购物车一般都是后台 ...

  4. Android开发之拍照功能实现(附源码)

    大家好,这是一个简单的拍照功能,很简单的界面,一个显示图像区域SurfaceView一个"拍照"按钮.直接上代码! 1.CameraDemoActivity.java(主界面) p ...

  5. android+usb模拟点击,Android后台模拟点击探索(附源码)

    工作中我们需要自制一套工具,其中遇到需要模拟点击事件的需求,类似按键精灵的功能,支持后台持续运行,满足触发条件时完成点击. 经过一番探索,一共整理出两种不同的方案:AccessibilityServi ...

  6. android 模拟点击某点,Android后台模拟点击探索(附源码)

    工作中我们需要自制一套工具,其中遇到需要模拟点击事件的需求,类似按键精灵的功能,支持后台持续运行,满足触发条件时完成点击. 经过一番探索,一共整理出两种不同的方案:AccessibilityServi ...

  7. android 手机壁纸源码,Android工程实现换壁纸功能【附源码】

    最近工作要实现换壁纸小功能,将代码做成demo发出来 没有采用zip格式换肤,因为只是更换一张图片背景 1.将三张图放入drawable-hdpi,我放的是480*800的 2.用sharedPref ...

  8. android+酷炫动画效果,Android简单酷炫点击动画(附源码)

    在Android5.0之前, Android的点击效果一直很low, 即使5.0的波纹效果也不尽如人意. 而我之前写过一种比较酷炫的点击效果, 最近抽了点时间完善一下, 把阴影效果加入了进去, 大家先 ...

  9. android广告SDK原理详解(附源码)

    广大的开发者吃糠咽菜开发了一两款APP,获取了一些流量后自然就会想到流量变现,一般情况下大家会选择到百度联盟或者Google Admob这些广告服务提供商注册开发者账号,在自己的APP里面加上几行广告 ...

最新文章

  1. nodejs下载安装教程(XP版)
  2. Merge into 用法
  3. html不居中代码,HTML – 为什么我不能居中div?
  4. 可能是世界上最简单的用 Go 来写 WebAssembly 的教程
  5. xml 属性value换行显示_跟光磊学Java开发-Java解析XML
  6. 新书正式定名《互联网运营智慧》
  7. Mozilla 将推出全新的安卓移动浏览器 Fenix
  8. 在linux下安装iNode校园客户端
  9. unix域套接字UDP网络编程
  10. Disable STRICT_TRANS_TABLES @Mysql 5.7
  11. php ddl,MySQL定义语言[DDL]
  12. mysql 大事物commit慢造成全库堵塞问题
  13. java多脚本顺序运行_关于eclipse里运行selenium脚本的顺序问题
  14. js异步之setTimeout与setInterval
  15. python爬网页html乱码问题
  16. 笔记本电脑(windows10)qq聊天时对方听不到自己的声音
  17. 计算机辅助药物设计的心得,计算机辅助药物设计实验的探索与心得.doc
  18. R3Det: Refined Single-Stage Detector with Feature Refinement for Rotating Object
  19. php 模态框效果,评论:超酷的模态框效果 - Nifty
  20. Oracle数据库之SQL函数

热门文章

  1. 全球及中国热电离质谱仪行业投资风险预测及发展规划建议报告2022-2028年
  2. 【转】VMware Workstation 8.0.1 build-528992精简绿色版
  3. 模板方法模式介绍与示例
  4. ubuntu 如何让桌面显示“我的电脑”及去掉桌面上的“磁盘图标”
  5. Keil5软件详细安装教程
  6. 初识sed和gawk
  7. css 金额千位符,css3 - 使用C格式化数字(小数位,千位分隔符等)
  8. SQL Server 2005中查询通知的开启
  9. JAVA实训项目之学生管理系统(JDBC+GUI)
  10. 2021-2027全球及中国半导体掩膜版行业研究及十四五规划分析报告