当前位置: 首页 > news >正文

Android-悬浮窗口

在Android系统中,如果应用需要弹出一个悬浮窗口,就需要申请一项特殊权限

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

在Android O之前的系统中申请了该权限后,再给对应的window设置

WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_PHONE;

悬浮窗口就可以显示出来。

但是在Android O的系统中,google规定申请

android.permission.SYSTEM_ALERT_WINDOW
权限的应用需要给悬浮窗口设置如下type:

WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

悬浮窗口才能显示出来,“TYPE_APPLICATION_OVERLAY”是重点。
如果不设置该TYPE,应用会Crash,报错如下(后面的2002表示设置的type为TYPE_PHONE):

AndroidRuntime: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@c8d1f1a -- permission denied for window type 2002

另外说一下:申请
android.permission.SYSTEM_ALERT_WINDOW
权限不能使用 requestPermissions 方法。
可以使用下面的方法:

Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 100);

完整代码

1、添加权限

在AndroidManifest.xml中添加悬浮窗所需的权限:

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

2、请求权限(针对Android 6.0及以上)

  • 对于Android 6.0(API级别23)及以上的设备,需要运行时请求SYSTEM_ALERT_WINDOW权限。示例:
private static final int REQUEST_CODE_DRAW_OVER_OTHER_APPS_PERMISSION = 200;// 在Activity或Fragment中请求权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName()));startActivityForResult(intent, REQUEST_CODE_DRAW_OVER_OTHER_APPS_PERMISSION);
}
  • 处理权限请求的结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == REQUEST_CODE_DRAW_OVER_OTHER_APPS_PERMISSION) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {// 权限被拒绝Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();} else {// 权限已授予,可以显示悬浮窗showFloatingWindow();}}
}

3、实现悬浮窗

  • 定义悬浮窗的布局和显示逻辑:
public void showFloatingWindow() {// 布局参数WindowManager.LayoutParams params;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,PixelFormat.TRANSLUCENT);} else {params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.TYPE_PHONE,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,PixelFormat.TRANSLUCENT);}// 设置悬浮窗的位置params.gravity = Gravity.TOP | Gravity.START; // 例如,设置在屏幕左上角params.x = 100; // x坐标params.y = 100; // y坐标// 创建浮动窗口视图LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);View floatingView = inflater.inflate(R.layout.floating_window_layout, null);// 获取WindowManagerWindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);try {// 添加悬浮窗到WindowManagerwindowManager.addView(floatingView, params);} catch (Exception e) {e.printStackTrace();}
}
  • 浮窗拖动
// 设置触摸监听器以实现拖动
floatingLayout.setOnTouchListener(new View.OnTouchListener() {private int initialX;private int initialY;private float initialTouchX;private float initialTouchY;@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 记录按下时的坐标initialX = params.x;initialY = params.y;initialTouchX = event.getRawX();initialTouchY = event.getRawY();break;case MotionEvent.ACTION_MOVE:// 计算移动的距离,并更新悬浮窗位置params.x = initialX + (int) (event.getRawX() - initialTouchX);params.y = initialY + (int) (event.getRawY() - initialTouchY);windowManager.updateViewLayout(floatingView, params);break;case MotionEvent.ACTION_UP:// 手指抬起,可以根据需要做一些操作,这里直接返回break;default:return false;}return true; // 消费掉这个事件,防止它被其他视图消费}
});
  • 关闭浮窗
public void removeFloatingWindow() {// 获取WindowManager实例WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);// 确保floatingView是之前添加到WindowManager的那个View实例// 这里假设floatingView是在showFloatingWindow()方法中创建并添加的View floatingView = ...; // 你需要确保这指向正确的View实例if (floatingView != null && windowManager != null) {// 移除悬浮窗视图windowManager.removeView(floatingView);}
}

悬浮窗开启关闭时前后台切换功能

app退到后台

方案一、启动Home页
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);

如果Launcher和Home不是同一个,就不能这么用。比如说机顶盒Launcher,启动第三方app都是从这里打开的。然后这里如果执行了上述代码,启动了Home,那就跳转到了Android系统的Home,就不是退到后台的效果了。

方案二、执行Activity#moveTaskToBack()
moveTaskToBack(false);
  • 关于moveTaskToBack的参数
/*** Move the task containing this activity to the back of the activity* stack.  The activity's order within the task is unchanged.** @param nonRoot If false then this only works if the activity is the root*                of a task; if true it will work for any activity in*                a task.** @return If the task was moved (or it was already at the*         back) true is returned, else false.*/public boolean moveTaskToBack(boolean nonRoot) {}
  • nonRoot=false时,只有当当前Activity为root activity根Activity时才会把当前task退回到后台。notRoot=true时,不管当前是否是root activity都会把当前task退回到后台。

app切到前台

方案一、使用Intent启动需要切到前台的Activity
Intent intent = new Intent(this, MainActivity.class);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setAction(Intent.ACTION_MAIN);
//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
startActivity(intent);
  • 这里的MainActivity.class就是需要启动的Activity
方案二、通过ActivityMananger把task切到前台
ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
am.moveTaskToFront(getTaskId(), ActivityManager.MOVE_TASK_WITH_HOME);
  • 报错信息
java.lang.SecurityException: Permission Denial: moveTaskToFront() from pid=20744, uid=10516 requires android.permission.REORDER_TASKS
  • 这个方法需要权限 android.permission.REORDER_TASKS
  • 可能影响Google上架
权限申请
<uses-permission android:name="android.permission.REORDER_TASKS"/>
// 检查是否已经有了权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.REORDER_TASKS)!= PackageManager.PERMISSION_GRANTED) {// 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.REORDER_TASKS)) {// 显示解释为什么需要这个权限的对话框,然后再次请求权限} else {// 没有权限,直接请求权限ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.REORDER_TASKS},MY_PERMISSIONS_REQUEST_REORDER_TASKS);}
} else {// 已经有权限,可以执行相应操作
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == MY_PERMISSIONS_REQUEST_REORDER_TASKS) {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {// 权限被用户同意,可以执行moveTaskToFront操作} else {// 权限请求被拒绝,根据情况处理,如提示用户权限重要性或提供备选方案}}
}
http://www.lryc.cn/news/390345.html

相关文章:

  • 打破僵局:Foxit Reader无法打开的终极解决方案
  • [调试] JTAG下运行正常,从QSPI或者SD卡启动则无响应,如何查找问题
  • Linux内核 -- 多线程之wait_event用法
  • 双指针系列第 8 篇:盛水最多的容器。几句话讲明白!
  • c++高阶-1-模板
  • .net core 的 winform 的 浏览器控件 WebView2
  • Django QuerySet对象,all()方法
  • 自动生成网站sitemap
  • 中国经济昆虫志(55卷)
  • linux环境安装elasticsearch缓存数据库和Kibana客户端
  • OpenSSL的一些使用案例
  • 常用字符串方法<python>
  • 线程池666666
  • Python28-5 k-means算法
  • 主流国产服务器操作系统技术分析
  • 【Linux】线程封装与互斥(万字)
  • 5分钟教你部署MySQL8.0环境
  • LLM应用:传统NLP任务
  • 基于Hadoop平台的电信客服数据的处理与分析③项目开发:搭建Kafka大数据运算环境---任务11:基础环境准备
  • Golang中swtich中如何强制执行下一个代码块
  • 读书笔记-Java并发编程的艺术-第4章(Java并发编程基础)-第2节(启动和终止线程)
  • 通俗大白话理解Docker
  • 题解:CF1981C(Turtle and an Incomplete Sequence)
  • Swift 中强大的 Key Paths(键路径)机制趣谈(上)
  • (十二)纹理和采样
  • QT创建地理信息shp文件编辑器shp_editor
  • 解析Kotlin中扩展函数与扩展属性【笔记摘要】
  • 【Java学习笔记】java图形界面编程
  • STM32入门笔记(03): ADC(SPL库函数版)(2)
  • 2024年7月2日 (周二) 叶子游戏新闻