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 {// 权限请求被拒绝,根据情况处理,如提示用户权限重要性或提供备选方案}}
}
相关文章:
Android-悬浮窗口
在Android系统中,如果应用需要弹出一个悬浮窗口,就需要申请一项特殊权限 <uses-permission android:name"android.permission.SYSTEM_ALERT_WINDOW"/>在Android O之前的系统中申请了该权限后,再给对应的window设置 WindowM…...
打破僵局:Foxit Reader无法打开的终极解决方案
打破僵局:Foxit Reader无法打开的终极解决方案 在数字化阅读时代,Foxit Reader作为一款广受欢迎的PDF阅读器,其打不开的问题无疑会给用户带来诸多不便。本文将为您提供全面的解决方案,从基础检查到高级技巧,确保您能够…...

[调试] JTAG下运行正常,从QSPI或者SD卡启动则无响应,如何查找问题
[调试] JTAG下运行正常,从QSPI或者SD卡启动则无响应,如何查找问题 一、问题现象二、用自定义fsbl替代系统默认的fsbl1. 新建fsbl_new2. 如果提示缺少xilffs库3. 使能调试信息输出 三. 启动成功和失败情况下的典型输出1. JTAG启动模式: 正常加载2. QSPI启…...
Linux内核 -- 多线程之wait_event用法
Linux Kernel 中 wait_event 的高级用法及注意事项 在Linux内核编程中,wait_event 系列函数是用于实现进程等待和事件通知机制的重要工具。本文将详细介绍 wait_event 的高级用法以及注意事项。 1. 基本用法 wait_event 系列宏主要包括以下几种形式: …...

双指针系列第 8 篇:盛水最多的容器。几句话讲明白!
Leetcode 题目链接 思路 取首尾双指针和水量如下所示,设高度函数为 h ( i ) h(i) h(i),在下图中 h ( l ) < h ( r ) h(l) < h(r) h(l)<h(r)。 观察以 l l l 为左边界所能构成的其他水量,与矮的右边界搭配结果如下。 与高的…...
c++高阶-1-模板
文章目录 模板一、模板基本语法二、函数模板1.基本语法2.函数模板注意事项3.普通函数和函数模板区别4.普通函数和函数模板调用规则 三、类模板1.基本语法2.类模板和函数模板的区别3.类模板中成员函数调用时机4.类模板对象做函数参数5.类模板与继承6.成员函数的类外实现 模板 一…...

.net core 的 winform 的 浏览器控件 WebView2
在.NET Core WinForms应用程序中,没有直接的“浏览器控件”,因为WinForms不支持像WebBrowser控件那样的功能。但是,你可以使用WebView2控件,它是一个基于Chromium的浏览器内核,可以在WinForms应用程序中嵌入Web内容。 …...

Django QuerySet对象,all()方法
all()方法 在Django中,all()方法是QuerySet对象的一个方法,用于获取模型的所有实例。 当你调用ModelName.objects.all()时,Django会生成一个SQL查询,从数据库中获取该模型的所有记录,并返回一个QuerySet对象…...
自动生成网站sitemap
要在 Next.js 和 Contentlayer 项目中实现自动生成 Sitemap 的功能,你可以编写一个脚本,在每次生成文档后自动生成 Sitemap。以下是一个示例脚本,你可以根据自己的需求进行调整。 步骤 1:安装必要的依赖 首先,你需要…...

中国经济昆虫志(55卷)
中国经济昆虫志,共55卷,内容包括概述、形态特征、分类等。各级分类单元均编有检索表,每个种有特征描述、地理分布,有的还记载有生活习性和防治方法。为便于鉴定,绘制有特征图和彩色图。 包括鞘翅目天牛科、半翅目蝽科、…...
linux环境安装elasticsearch缓存数据库和Kibana客户端
linux环境安装elasticsearch缓存数据库,今天我们安装7.17.18版本,并分析遇到的问题。 一、elasticsearch安装运行 1、直接下载 wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.18-linux-x86_64.tar.gz2、解压 tar -…...

OpenSSL的一些使用案例
目录 一、介绍 二、基本使用 1、Shell (1)文件加解密 (2)生成密钥文件 2、API (1)md5sum (2)AES256加解密 一、介绍 本篇博客重点不是详细描述 OpenSSL 的用法,只…...

常用字符串方法<python>
导言 在python中内置了许多的字符串方法,使用字符串方法可以方便快捷解决很多问题,所以本文将要介绍一些常用的字符串方法。 目录 导言 string.center(width[,fillchar]) string.capitalize() string.count(sub[,start[,end]]) string.join(iterabl…...

线程池666666
1. 作用 线程池内部维护了多个工作线程,每个工作线程都会去任务队列中拿取任务并执行,当执行完一个任务后不是马上销毁,而是继续保留执行其它任务。显然,线程池提高了多线程的复用率,减少了创建和销毁线程的时间。 2…...

Python28-5 k-means算法
k-means 算法介绍 k-means 算法是一种经典的聚类算法,其目的是将数据集分成 ( k ) 个不同的簇,每个簇内的数据点尽可能接近。算法的基本思想是通过反复迭代优化簇中心的位置,使得每个簇内的点与簇中心的距离之和最小。k-means 算法的具体步骤…...

主流国产服务器操作系统技术分析
主流国产服务器操作系统 信创 "信创",即信息技术应用创新,作为科技自立自强的核心词汇,在我国信息化建设的进程中扮演着至关重要的角色。自2016年起步,2020年开始蓬勃兴起,信创的浪潮正席卷整个信息与通信技…...

【Linux】线程封装与互斥(万字)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 文章目录 前言 C多线程的用法 对原生线程进行一次封装 理解pthread线程 Linux线程互斥 进程线程间的互斥相关背景概念 互斥量mutex 操作共享变量会有问题的售票…...

5分钟教你部署MySQL8.0环境
此方法基于Windows操作系统! 一、在MySQL官网单击downloads(下载)MySQLhttps://www.mysql.com/cn/ 选择在Windows操作系统下载 二、选择合适的版本 推荐下载第二种,安装时离线安装即可 三、安装MySQL8.0 1、找到MySQL下载完成…...

LLM应用:传统NLP任务
LLM出来以后,知乎上就出现了“传统NLP已死”的言论,但是传统NLP真的就被扔进历史的垃圾桶了吗? 其实,尽管LLM具有出色的通用能力,但仍然无法有效应对低资源领域的自然语言处理任务,如小语种翻译。为了更好地…...
基于Hadoop平台的电信客服数据的处理与分析③项目开发:搭建Kafka大数据运算环境---任务11:基础环境准备
任务描述 任务主要是安装配置基础环境,主要内容包括: 1、安装java Kafka和ZooKeeper都需要安装Java环境,推荐至少Java8及以上版本 2、安装ZooKeeper ZooKeeper是Kafka集群的必要组件 3、安装kafka Kafka版本包括使用的scala语言版本和kafka版…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...