Android悬浮窗实现步骤

最近想做一个悬浮窗秒表的功能,所以看下悬浮窗具体的实现步骤

1、初识WindowManager

实现悬浮窗主要用到的是WindowManager

@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {...
}

WindowManager是接口类,继承自接口ViewManager,可以通过获取WINDOW_SERVICE系统服务得到。而ViewManager接口有addView方法,我们就是通过这个方法将悬浮窗控件加入到屏幕中去。

2、设置权限

当API Level >= 23,显示悬浮窗功能,需要在清单文件AndroidManifest.xml中添加SYSTEM_ALERT_WINDOW权限,添加这个权限后才可以在其他应用上显示悬浮窗。

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

通过getSystemService方式获取WindowManager

val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager

3、LayoutParam设置

WindowManager的addView方法有两个参数,一个是需要加入的控件对象,另一个参数是WindowManager.LayoutParams对象。

	// view – The view to be added to this window.// params – The LayoutParams to assign to view.public void addView(View view, ViewGroup.LayoutParams params);

其中LayoutParams的type变量,这个变量是用来指定窗口的类型。在设置这个变量时,需要对不同版本的Android系统进行适配。

        val layoutParams = WindowManager.LayoutParams()if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY} else {layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE}

在Android 8.0之前,悬浮窗口设置可以为TYPE_PHONE,这种类型是用于提供用户交互操作的非应用窗口,现在这个类型已弃用了。
而Android 8.0对系统和API行为做了修改,包括使用SYSTEM_ALERT_WINDOW权限的应用无法再使用窗口类型来在其他应用和窗口上方显示提醒窗口:

  • TYPE_PHONE(已弃用)

  • TYPE_PRIORITY_PHONE

  • TYPE_SYSTEM_ALERT

  • TYPE_SYSTEM_OVERLAY

  • TYPE_SYSTEM_ERROR

如果需要实现在其他应用和窗口上方显示提醒窗口,那么必须该为TYPE_APPLICATION_OVERLAY的新类型。

4、检测是否允许开启悬浮窗

开启悬浮窗之前,还需要检测用户是否允许开启悬浮窗,通过系统提供的canDrawOverlays来检测

//检测是否允许开启悬浮窗
Settings.canDrawOverlays(context)

如果没有允许开启,需要跳转开启页面,让用户允许开启悬浮窗

startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION))

5、FloatingService服务

悬浮窗一直显示在其他应用上层,需要新建FloatingService服务类,用于处理悬浮窗相关逻辑。

class FloatingService : Service() {override fun onCreate() {super.onCreate()}override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {showFloatingWindow();return super.onStartCommand(intent, flags, startId)}override fun onBind(intent: Intent?): IBinder? {return null}/*** 显示悬浮窗*/private fun showFloatingWindow() {// 获取WindowManager服务val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager// 新建悬浮窗控件val button = Button(applicationContext)button.text = "Floating Window"button.setBackgroundColor(Color.BLUE)// 设置LayoutParamval layoutParams = WindowManager.LayoutParams()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.RGBA_8888layoutParams.flags =WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLElayoutParams.width = ActionBar.LayoutParams.WRAP_CONTENTlayoutParams.height = ActionBar.LayoutParams.WRAP_CONTENTlayoutParams.x = 300layoutParams.y = 300// 将悬浮窗控件添加到WindowManagerwindowManager.addView(button, layoutParams);}
}

6、启动FloatingService

       viewBinding.btnFloating.setOnClickListener {if (Settings.canDrawOverlays(this)) {//检测是否具有悬浮窗权限startService(Intent(this,FloatingService::class.java))} else {startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));}}

开启效果如下:
在这里插入图片描述

7、增加拖动功能

悬浮窗显示的位置可能会遮挡其他信息,这时就需要新增拖动功能,可以拖动到任何位置,实现的逻辑就是给布局View添加触摸事件,根据触摸和移动的位置来决定悬浮窗显示的位置。

        var x = 0var y = 0button.setOnTouchListener { view, event ->when (event.action) {MotionEvent.ACTION_DOWN -> {x = event.rawX.toInt()y = event.rawY.toInt()}MotionEvent.ACTION_MOVE -> {val nowX = event.rawX.toInt()val nowY = event.rawY.toInt()val movedX = nowX - xval movedY = nowY - yx = nowXy = nowYlayoutParams.x = layoutParams.x + movedXlayoutParams.y = layoutParams.y + movedY// 更新悬浮窗控件布局windowManager.updateViewLayout(view, layoutParams)}else -> {}}false}

8、图片自动播放

效果图如下:
在这里插入图片描述

页面布局layout_floating_image.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="wrap_content"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/imgView"android:layout_width="wrap_content"android:contentDescription="@null"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent"android:layout_height="wrap_content"/></androidx.constraintlayout.widget.ConstraintLayout>

FloatingImageService服务如下:

class FloatingImageService : Service() {override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {showFloatingWindow();return super.onStartCommand(intent, flags, startId)}override fun onBind(intent: Intent?): IBinder? {return null}/*** 显示悬浮窗*/@SuppressLint("ClickableViewAccessibility", "InflateParams")private fun showFloatingWindow() {// 获取WindowManager服务val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager// 获取悬浮窗布局val viewBinding = LayoutFloatingImageBinding.inflate(LayoutInflater.from(this))val imageArray = intArrayOf(R.drawable.pic1, R.drawable.pic2, R.drawable.pic3)var imageIndex = 0viewBinding.imgView.setImageResource(imageArray[imageIndex])val job = Job()val scope = CoroutineScope(job)scope.launch {while (true) {delay(2000)imageIndex++if (imageIndex == imageArray.size) {imageIndex = 0}withContext(Dispatchers.Main) {viewBinding.imgView.setImageResource(imageArray[imageIndex])}}}// 设置LayoutParamval layoutParams = WindowManager.LayoutParams()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.RGBA_8888layoutParams.gravity = Gravity.START or Gravity.TOPlayoutParams.flags =WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLElayoutParams.width = ActionBar.LayoutParams.WRAP_CONTENTlayoutParams.height = ActionBar.LayoutParams.WRAP_CONTENTlayoutParams.x = 0layoutParams.y = 0// 将悬浮窗控件添加到WindowManagerwindowManager.addView(viewBinding.root, layoutParams)var x = 0var y = 0viewBinding.root.setOnTouchListener { view, event ->when (event.action) {MotionEvent.ACTION_DOWN -> {x = event.rawX.toInt()y = event.rawY.toInt()}MotionEvent.ACTION_MOVE -> {val nowX = event.rawX.toInt()val nowY = event.rawY.toInt()val movedX = nowX - xval movedY = nowY - yx = nowXy = nowYlayoutParams.x = layoutParams.x + movedXlayoutParams.y = layoutParams.y + movedY// 更新悬浮窗控件布局windowManager.updateViewLayout(view, layoutParams)}else -> {}}false}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/658342.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL-窗口函数

介绍&#xff1a; MSQL8.0新增窗口函数商口函数又被称为开窗函数&#xff0c;与Oracle窗口函数类似&#xff0c;属于MysaL的一大特点 非聚合窗口函数是相对于聚函数来说的。聚合函数是对一组数据计算后返回单个值(即分组)&#xff0c;非聚合函数一次只会处理一行数据。窗口聚…

Unity项目打包的方法(之一)

在 Unity 中&#xff0c;将项目打包成 .unitypackage 文件和直接压缩 Assets、Packages 和 ProjectSettings 目录有几个关键区别&#xff0c;主要体现在打包方式、使用目的和包含的内容上。 打包成 UnityPackage .unitypackage 是 Unity 的一种打包格式&#xff0c;它允许你将项…

leetcode—划分字母区间—贪心算法

题目描述 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。…

盲盒小程序开发,实现“双收益”

盲盒在我国是一个发展潜力较高的市场&#xff0c;盲盒具有的刺激和收藏价值&#xff0c;深受消费者的喜爱&#xff0c;盲盒的“隐藏款”机制&#xff0c;能够为消费者带来惊喜感。盲盒一般与影视动漫IP合作&#xff0c;推出盲盒商品&#xff0c;这也是深受年轻人追捧的一大特点…

Redis内存设置

通过redis-cli进入Redis命令行 redis权限认证命令&#xff1a;auth 查看redis内存使用情况的命令&#xff1a;info memory 查看最大内存命令&#xff1a;config get maxmemory 设置最大内存命令&#xff1a;config set maxmemory 也可以通过redis.conf配置文件修改最大内存…

Cache Lab:Part A【模拟出使用LRU策略的高速缓存存储器组织结构】

目录 任务描述 知识回顾 实验内容 测试结果 Cache Lab 对应《CS:APP》6.3节至第六章结束的内容。 任务描述 Your job for Part A is to fill in the csim.c file so that it takes the same command line arguments and produces the identical output as the reference …

MySQL事务和SQL优化

目录 1 什么是事务 2 事务的特征 3 MySQL使用事务 实现 示例 4 事务的隔离级别 幻读 解决方法 脏读 不可重复读 幻读和不可重复读两者区别 事物的隔离级别 5 数据库优化 5.1 影响性能因素的优化 服务优化 应用优化 5.2 谁参与优化 5.3 系统优化 软件优化 硬件优…

人力RPO蓝海项目可靠性有多大?

随着全球经济的快速发展&#xff0c;人力资源外包服务逐渐成为企业降低成本、提高效率的重要手段。其中&#xff0c;RPO(招聘流程外包)作为人力资源外包的一种形式&#xff0c;日益受到企业的青睐。然而&#xff0c;对于RPO的可靠性&#xff0c;业界和学界存在不同的看法。本文…

宏景eHR FrCodeAddTreeServlet SQL注入漏洞复现

前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 一、产…

Spring Boot接收xml参数

学无止境&#xff0c;气有浩然! Spring Boot接收xml参数 前言方案实现代码示例 打完收工&#xff01; 前言 最近工作又要做挡板&#xff0c;挡板的东西有点多&#x1f600;&#xff0c;主要是因为下游是第三方收费的&#xff0c;提供的测试环境的数据不能随心所欲修改&#xf…

mybatisplus-多数据源配置

1. 流程 pom文件yml配置多数据源具体服务添加注解DS(“***”) 1.pom文件 <!--mybatis plus 起步依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</vers…

万户 ezOFFICE DocumentEditExcel.jsp SQL注入漏洞

0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品,统一的基础管理平台,实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台,将外网信息维护、客户服务、互动交流和日…

STM32——智能小车

STM32——智能小车 硬件接线 B-1A – PB0 B-1B – PB1 A-1A – PB2 A-1B – PB10 其余接线参考51单片机小车项目。 1.让小车动起来 motor.c #include "motor.h" void goForward(void) {// 左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO…

一文总结Java的23种设计模式

Java 设计模式是 Java 编程中常用的一些解决问题的模板&#xff0c;它们代表了最佳的实践和经验。设计模式可以帮助我们编写可复用、可维护和可扩展的代码。根据《设计模式&#xff1a;可复用面向对象软件的基础》一书&#xff0c;设计模式主要分为三大类&#xff1a;创建型模式…

js对表格table行排序

function sortTable(type) {// 获取所有行并按第1列进行排序var sortedRows $(#myTbody tr).sort(function (a, b) {// 获取需要排序的列的值&#xff0c;可能是数字或者字符&#xff0c;这个根据自己的需要var indexa Number.parseInt($(a).find(td:eq(0)).text());var inde…

fetch和axios的区别

概念不同 Fetch是一种新的获取资源的接口方式&#xff0c;可以直接使用Axios是一个基于XMLHttpRequest封装的工具包&#xff0c;需要引入才可以使用 传递数据的方式不同 Fetch则是需要放在body属性中&#xff0c;以字符串的方式进行传递Axios是放到data属性里&#xff0c;以对象…

STM32F407移植OpenHarmony笔记6

继上一篇笔记&#xff0c;编译好STM32的裸机程序&#xff0c;能点亮LED灯了。 下一步就是启动liteos_m内核了。 不过为了更好的调试代码&#xff0c;需要先把printf重定向到串口&#xff0c;基于gcc的printf重定向和Keil不一样。 直接新建printf.c&#xff0c;在里面重写printf…

笔记本从零安装ubuntu server系统+环境配置

文章目录 前言相关链接ubuntu Server 安装教程屏幕自动息屏关上盖子不休眠MobaXterm外网SSH内网穿透IPV6远程 为什么我要笔记本装Linux为什么要换ubuntu Server版能否连接wifi之后Linux 配置清单总结 前言 之前装了个ubuntu desktop 版&#xff0c;发现没有命令行&#xff0c;…

呼吸灯--FPGA

目录 1.breath_led.v 2.tb_breath_led.v 呼吸灯就是从完全熄灭到完全点亮&#xff0c;再从完全点亮到完全熄灭。具体就是通过控制PWM的占空比控制亮灭程度。 绘制PWM波的步骤就是&#xff0c;首先灯是在第一个时钟周期保持高电平熄灭状态&#xff0c;在第二个时钟周期保持1/1…

RabbitMQ入门概念

目录 一、RabbitMQ入门 1.1 rabbitmq是啥&#xff1f; 1.2 应用场景 1.3 AMQP协议与RabbitMQ工作流程 1.4 Docker安装部署RabbitMQ 二、SpringBoot连接MQ配置 2.1 示例1 2.1 示例2 —— 发送实体 一、RabbitMQ入门 1.1 rabbitmq是啥&#xff1f; MQ&#xff08;Message…