我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Kotlin实现Android系统悬浮窗详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Kotlin实现Android系统悬浮窗详解

Android 弹窗浅谈

我们知道 Android 弹窗中,有一类弹窗会在应用之外也显示,这是因为他被申明成了系统弹窗,除此之外还有2类弹窗分别是:子弹窗应用弹窗

应用弹窗:就是我们常规使用的 Dialog 之类弹窗,依赖于应用的 Activity;子弹窗:依赖于父窗口,比如 PopupWindow;系统弹窗:比如状态栏、Toast等,本文所讲的系统悬浮窗就是系统弹窗。

系统悬浮窗具体实现

权限申请


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

代码设计

下面的包结构截图,简单展示了实现系统悬浮窗的代码结构,更复杂的业务需要可在此基础上进行扩展。

FloatWindowService:系统悬浮窗在此 Service 中弹出;

FloatWindowManager:系统悬浮窗管理类;

FloatLayout:系统悬浮窗布局;

HomeKeyObserverReceiver:

监听 Home 键;

FloatWindowUtils:系统悬浮窗工具类。

具体实现

FloatWindowService 类


class FloatWindowService : Service() {
 
    private val TAG = FloatWindowService::class.java.simpleName
    private var mFloatWindowManager: FloatWindowManager? = null
    private var mHomeKeyObserverReceiver: HomeKeyObserverReceiver? = null
 
    override fun onCreate() {
        TLogUtils.i(TAG, "onCreate: ")
        mFloatWindowManager = FloatWindowManager(applicationContext)
        mHomeKeyObserverReceiver = HomeKeyObserverReceiver()
 
        registerReceiver(mHomeKeyObserverReceiver, IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
        mFloatWindowManager!!.createWindow()
    }
 
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
 
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return START_NOT_STICKY
    }
 
    override fun onDestroy() {
        TLogUtils.i(TAG, "onDestroy: ")
        mFloatWindowManager?.removeWindow()
        if (mHomeKeyObserverReceiver != null) {
            unregisterReceiver(mHomeKeyObserverReceiver)
        }
    }
 
}

FloatWindowManager 类

包括系统悬浮窗的创建、显示、销毁(以及更新)。


 
public void addView(View view, ViewGroup.LayoutParams params); // 添加 View 到 Window
public void updateViewLayout(View view, ViewGroup.LayoutParams params); //更新 View 在 Window 中的位置
public void removeView(View view); //删除 View

FloatWindowManager 类代码


class FloatWindowManager constructor(context: Context) {
 
    var isShowing = false
    private val TAG = FloatWindowManager::class.java.simpleName
    private var mContext: Context = context
    private var mFloatLayout = FloatLayout(mContext)
    private var mLayoutParams: WindowManager.LayoutParams? = null
    private var mWindowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
 
    fun createWindow() {
        TLogUtils.i(TAG, "createWindow: start...")
        // 对象配置操作使用apply,额外的处理使用also
        mLayoutParams = WindowManager.LayoutParams().apply {
            type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                // Android 8.0以后需要使用TYPE_APPLICATION_OVERLAY,不允许使用以下窗口类型来在其他应用和窗口上方显示提醒窗口:TYPE_PHONE、TYPE_PRIORITY_PHONE、TYPE_SYSTEM_ALERT、TYPE_SYSTEM_OVERLAY、TYPE_SYSTEM_ERROR。
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
            } else {
                // 在Android 8.0之前,悬浮窗口设置可以为TYPE_PHONE,这种类型是用于提供用户交互操作的非应用窗口。
                // 在API Level  = 23的时候,需要在Android Manifest.xml文件中声明权限SYSTEM_ALERT_WINDOW才能在其他应用上绘制控件
                WindowManager.LayoutParams.TYPE_PHONE
            }
            // 设置图片格式,效果为背景透明
            format = PixelFormat.RGBA_8888
            // 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
            flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
            // 调整悬浮窗显示的停靠位置为右侧置顶
            gravity = Gravity.TOP or Gravity.END
            width = 800
            height = 200
            x = 20
            y = 40
        }
 
        mWindowManager.addView(mFloatLayout, mLayoutParams)
        TLogUtils.i(TAG, "createWindow: end...")
        isShowing = true
    }
 
    fun showWindow() {
        TLogUtils.i(TAG, "showWindow: isShowing = $isShowing")
        if (!isShowing) {
            if (mLayoutParams == null) {
                createWindow()
            } else {
                mWindowManager.addView(mFloatLayout, mLayoutParams)
                isShowing = true
            }
        }
    }
 
    fun removeWindow() {
        TLogUtils.i(TAG, "removeWindow: isShowing = $isShowing")
        mWindowManager.removeView(mFloatLayout)
        isShowing = false
    }
 
}

FloatLayout 类及其 Layout

系统悬浮窗自定义View:FloatLayout


class FloatLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) :
    ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) {
 
    private var mTime: TCLTextView
    private var mDistance: TCLTextView
    private var mSpeed: TCLTextView
    private var mCalories: TCLTextView
 
    init {
        val view = LayoutInflater.from(context).inflate(R.layout.do_exercise_view_float_layout, this, true)
        mTime = view.findViewById(R.id.float_layout_tv_time)
        mDistance = view.findViewById(R.id.float_layout_tv_distance)
        mSpeed = view.findViewById(R.id.float_layout_tv_speed)
        mCalories = view.findViewById(R.id.float_layout_tv_calories)
    }
 
}

布局文件:float_layout_tv_time

HomeKeyObserverReceiver 类


class HomeKeyObserverReceiver : BroadcastReceiver() {
 
    override fun onReceive(context: Context?, intent: Intent?) {
        try {
            val action = intent!!.action
            val reason = intent.getStringExtra("reason")
            TLogUtils.d(TAG, "HomeKeyObserverReceiver: action = $action,reason = $reason")
            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS == action && "homekey" == reason) {
                val keyCode = intent.getIntExtra("keycode", KeyEvent.KEYCODE_UNKNOWN)
                TLogUtils.d(TAG, "keyCode = $keyCode")
                context?.stopService(Intent(context, FloatWindowService::class.java))
            }
        } catch (ex: Exception) {
            ex.printStackTrace()
        }
    }
 
    companion object {
        private val TAG = HomeKeyObserverReceiver::class.java.simpleName
    }
 
}

FloatWindowUtils 类


object FloatWindowUtils {
 
    const val REQUEST_FLOAT_CODE = 1000
    private val TAG = FloatWindowUtils::class.java.simpleName
 
    
    fun isServiceRunning(context: Context, ServiceName: String): Boolean {
        if (TextUtils.isEmpty(ServiceName)) {
            return false
        }
        val myManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val runningService = myManager.getRunningServices(1000) as ArrayList<ActivityManager.RunningServiceInfo>
        runningService.forEach {
            if (it.service.className == ServiceName) {
                return true
            }
        }
        return false
    }
 
    
    @SuppressLint("NewApi")
    fun checkSuspendedWindowPermission(context: Activity, block: () -> Unit) {
        if (commonROMPermissionCheck(context)) {
            block()
        } else {
            Toast.makeText(context, "请开启悬浮窗权限", Toast.LENGTH_SHORT).show()
            context.startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
                data = Uri.parse("package:${context.packageName}")
            }, REQUEST_FLOAT_CODE)
        }
    }
 
    
    fun commonROMPermissionCheck(context: Context?): Boolean {
        var result = true
        if (Build.VERSION.SDK_INT >= 23) {
            try {
                val clazz: Class<*> = Settings::class.java
                val canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context::class.java)
                result = canDrawOverlays.invoke(null, context) as Boolean
            } catch (e: Exception) {
                TLogUtils.e(TAG, e)
            }
        }
        return result
    }
 
}

总结

本文并未详细讨论系统悬浮窗的拖动功能,实现系统悬浮穿基本功能可以总结为以下几个步骤:

1. 声明及申请权限;
2. 构建悬浮窗需要的控件 Service、Receiver、Manager、Layout、Util;
3. 使用 WindowManager 创建、显示、销毁(以及更新)Layout。

到此这篇关于Kotlin实现Android系统悬浮窗详解的文章就介绍到这了,更多相关Android Kotlin系统悬浮窗内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Kotlin实现Android系统悬浮窗详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Kotlin如何实现Android系统悬浮窗

本篇内容介绍了“Kotlin如何实现Android系统悬浮窗”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Android 弹窗浅谈我们知道
2023-06-22

Android悬浮窗如何实现

小编给大家分享一下Android悬浮窗如何实现,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!效果如下:显示浮窗原生ViewManager接口提供了向窗口添加并操纵
2023-06-04

详解android6.0版本下悬浮窗实现

悬浮窗在安卓中实现起来还是比较容易的,这几天在网上温习了相关资料,运行在我安卓6.0手机上才发现,原来在6.0手机上不是行的。第一反应肯定是权限相关问题,做了相关处理后,果然让悬浮窗原形毕露了。直接贴代码。public class Main
2022-06-06

Android实现系统级悬浮按钮

本文实例为大家分享了Android系统级悬浮按钮的具体代码,供大家参考,具体内容如下 具体的需求1、就是做一个系统级的悬浮按钮,就像iPhone 桌面的那个悬浮按钮效果一样,能随意拖动,并且手一放开,悬浮按钮就自动靠边。 2、可以点击并且可
2022-06-06

Android悬浮窗效果怎么实现

要实现Android的悬浮窗效果,可以采用以下几种方法:使用系统提供的WindowManager类来创建一个悬浮窗口。可以通过以下步骤实现:在AndroidManifest.xml文件中添加SYSTEM_ALERT_WINDOW权限。创建
2023-10-22

C#实现悬浮窗口的方法详解

这篇文章主要为大家详细介绍了C#如何实现悬浮窗口的相关资料,文中的示例代码讲解详细,对我们学习C#有一定的帮助,感兴趣的小伙伴可以了解一下
2022-12-21

Android WindowManger实现桌面悬浮窗功能

这篇文章主要介绍了Android WindowManger实现桌面悬浮窗功能,他们基本都是在Activity之上显示的,如果想实现在桌面显示的悬浮窗效果,需要用到WindowManager来实现了,需要的朋友可以参考下
2023-05-18

Android可拖动悬浮窗怎么实现

要实现在Android中可拖动的悬浮窗,可以按照以下步骤进行:1. 创建一个自定义的`FloatingView`类来实现悬浮窗的视图。在该类中,你可以添加任何你想要显示的视图元素,如文字、图像等。2. 在`FloatingView`类中,重
2023-08-16

Android应用内悬浮窗Activity如何实现

这篇文章主要介绍Android应用内悬浮窗Activity如何实现,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!缩放方法缩放activity需要使用WindowManager.LayoutParams,控制windo
2023-06-22

android编程实现悬浮窗体的方法

本文实例讲述了android编程实现悬浮窗体的方法。分享给大家供大家参考,具体如下: 突然对悬浮窗体感兴趣,查资料做了个小Demo,效果是点击按钮后,关闭当前Activity,显示悬浮窗口,窗口可以拖动,双击后消失。效果图如下:它的使用原理
2022-06-06

Android 项目必备(四十四)-->Android 实现悬浮窗

前言 悬浮窗是一种比较常见的需求。例如把视频通话界面缩小成一个悬浮窗,然后用户可以在其他界面上处理事情。 本文将讲解悬浮窗实现步骤、原理、实例代码等 实现原理 1. WindowMananger 接口 Android 的界面绘制,都是通过
2023-08-16

详解如何在Android中实现悬浮Activity

通常来说,为一款已经优化过的手机APP开发平板的版本也不是很难。使用Fragment、decompose Entities等组件就可以又快又轻松地完成。但是,最近遇到一个项目就没有这么简单了。我们不只要开发一个平板APP(基于ActionB
2022-06-06

Android实现带磁性的悬浮窗体效果

本文实例讲述了Android实现带磁性的悬浮窗体效果。分享给大家供大家参考,具体如下: 带磁性的悬浮窗体,类似于360绿色小人 主要实现的是: 1.悬浮所有窗体之上 2.有吸引力,吸附于屏幕边上 3.有点击效果 下面我就实现上面三点,简单封
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录