Kotlin如何实现Android系统悬浮窗
发表于:2025-01-21 作者:千家信息网编辑
千家信息网最后更新 2025年01月21日,本篇内容介绍了"Kotlin如何实现Android系统悬浮窗"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有
千家信息网最后更新 2025年01月21日Kotlin如何实现Android系统悬浮窗
本篇内容介绍了"Kotlin如何实现Android系统悬浮窗"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
Android 弹窗浅谈
我们知道 Android 弹窗中,有一类弹窗会在应用之外也显示,这是因为他被申明成了系统弹窗,除此之外还有2类弹窗分别是:子弹窗与应用弹窗。
应用弹窗:就是我们常规使用的 Dialog 之类弹窗,依赖于应用的 Activity;子弹窗:依赖于父窗口,比如 PopupWindow;系统弹窗:比如状态栏、Toast等,本文所讲的系统悬浮窗就是系统弹窗。
系统悬浮窗具体实现
权限申请
代码设计
下面的包结构截图,简单展示了实现系统悬浮窗的代码结构,更复杂的业务需要可在此基础上进行扩展。
FloatWindowService:系统悬浮窗在此 Service 中弹出;
FloatWindowManager:系统悬浮窗管理类;
FloatLayout:系统悬浮窗布局;
HomeKeyObserverReceiver:
监听 Home 键;
FloatWindowUtils:系统悬浮窗工具类。
具体实现
FloatWindowService 类
class FloatWindowService : Service() { privateval 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 到 Windowpublic void updateViewLayout(View view, ViewGroup.LayoutParams params); //更新 View 在 Window 中的位置public void removeView(View view); //删除 View
FloatWindowManager 类代码
class FloatWindowManager constructor(context: Context) { var isShowing = false privateval 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 { privateval TAG = HomeKeyObserverReceiver::class.java.simpleName } }
FloatWindowUtils 类
object FloatWindowUtils { const val REQUEST_FLOAT_CODE = 1000 privateval TAG = FloatWindowUtils::class.java.simpleName /** * 判断Service是否开启 */ 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 ArrayListrunningService.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 } }
"Kotlin如何实现Android系统悬浮窗"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!
系统
应用
权限
代码
位置
内容
子弹
就是
布局
文件
更多
知识
类型
结构
更新
浮动
复杂
实用
学有所成
接下来
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
怎么自学网络技术
郑州最大的软件开发
刀剑斗神传s5的所有服务器
威尔梅斯网络技术
内存数据库概述PPT
安徽企业软件开发成本价
使用jndi连接数据库
it软件开发工程师薪资
网络安全日常化
数据库复制技术要解决的问题
网络安全基础实操
php数据库读取与写入
vr软件开发 平台
外包网络安全项目实施方案
网络安全机制主要内容
手机号码怎么查别人大数据库
国家坚持网络安全与发展并重
数据库中电话号码
网络安全技术吧
我国互联网科技企业
数据库多个表搜索
旗帜数据库提示系统不兼容
网络安全陈艺斌
三明市公安局网络安全主任
网络安全国家发展
济南医院软件开发公司
微型计算机中使用的数据库属于
邮件服务器类型怎么修改
比较毒理数据库
富士康服务器内存报错如何查看