EasyFloat实战案例:从零构建完整的悬浮窗应用

news2026/3/19 18:01:15
EasyFloat实战案例从零构建完整的悬浮窗应用【免费下载链接】EasyFloat EasyFloat浮窗从未如此简单Android可拖拽悬浮窗口支持页面过滤、自定义动画可设置单页面浮窗、前台浮窗、全局浮窗浮窗权限按需自动申请...项目地址: https://gitcode.com/gh_mirrors/ea/EasyFloatAndroid悬浮窗开发从未如此简单EasyFloat是一个功能强大、易于使用的Android悬浮窗框架支持页面过滤、自定义动画、拖拽吸附等丰富功能。本文将带你从零开始通过实战案例全面掌握EasyFloat悬浮窗框架的核心功能和应用场景。一、EasyFloat框架简介与核心优势EasyFloat是专为Android开发者设计的悬浮窗解决方案它解决了传统悬浮窗开发中的权限管理、生命周期控制、拖拽交互等复杂问题。该框架支持单页面浮窗、前台浮窗和全局浮窗三种显示模式能够满足不同业务场景的需求。核心功能特性零配置启动无需初始化一行代码即可创建悬浮窗️智能权限管理自动检测和申请悬浮窗权限灵活的显示模式支持Activity内、前台和全局显示丰富的动画效果内置默认动画支持自定义动画策略智能拖拽吸附15种吸附模式支持边界限制高度可定制支持自定义布局、事件回调、状态监听二、项目集成与环境搭建2.1 添加依赖配置首先在项目的根目录build.gradle文件中添加JitPack仓库allprojects { repositories { maven { url https://jitpack.io } } }然后在应用模块的build.gradle中添加EasyFloat依赖dependencies { implementation com.github.princekin-f:EasyFloat:2.0.4 }2.2 权限声明配置根据使用场景在AndroidManifest.xml中添加相应权限!-- 使用系统浮窗时需要此权限 -- uses-permission android:nameandroid.permission.SYSTEM_ALERT_WINDOW /三、基础悬浮窗创建实战3.1 最简单的悬浮窗实现创建基础悬浮窗只需一行代码// 创建并显示一个简单的悬浮窗 EasyFloat.with(this) .setLayout(R.layout.float_app) .show()图EasyFloat支持多种悬浮窗类型和完整的生命周期回调管理3.2 完整配置的悬浮窗示例下面是一个包含完整配置的悬浮窗实现EasyFloat.with(this) .setLayout(R.layout.float_app) { view - // 初始化视图组件 view.findViewByIdImageView(R.id.ivClose).setOnClickListener { EasyFloat.dismiss() } view.findViewByIdTextView(R.id.tvContent).text 欢迎使用EasyFloat } .setShowPattern(ShowPattern.ALL_TIME) // 全局显示 .setSidePattern(SidePattern.RESULT_HORIZONTAL) // 水平吸附 .setTag(main_float) // 设置标签用于区分多个浮窗 .setDragEnable(true) // 启用拖拽 .setGravity(Gravity.END or Gravity.CENTER_VERTICAL, 0, 200) // 初始位置 .setBorder(100, 100, 800, 800) // 设置拖拽边界 .registerCallback { createResult { isCreated, msg, _ - Toast.makeText(thisMainActivity, 悬浮窗创建${if(isCreated) 成功 else 失败}: $msg, Toast.LENGTH_SHORT).show() } show { Log.d(EasyFloat, 悬浮窗显示) } hide { Log.d(EasyFloat, 悬浮窗隐藏) } dismiss { Log.d(EasyFloat, 悬浮窗关闭) } drag { view, motionEvent - // 拖拽过程中的处理 view.findViewByIdTextView(R.id.tvContent).text 拖拽中... } dragEnd { // 拖拽结束处理 it.findViewByIdTextView(R.id.tvContent).text 拖拽结束 } } .show()四、高级功能实战应用4.1 多悬浮窗管理策略在实际应用中我们经常需要管理多个悬浮窗。EasyFloat通过标签系统完美支持这一需求// 创建第一个悬浮窗 EasyFloat.with(this) .setLayout(R.layout.float_notification) .setTag(notification_float) .setShowPattern(ShowPattern.ALL_TIME) .show() // 创建第二个悬浮窗 EasyFloat.with(this) .setLayout(R.layout.float_quick_action) .setTag(quick_action_float) .setShowPattern(ShowPattern.FOREGROUND) .setLocation(100, 100) .show() // 单独控制特定悬浮窗 EasyFloat.hide(notification_float) // 隐藏通知悬浮窗 EasyFloat.show(quick_action_float) // 显示快捷操作悬浮窗 EasyFloat.dismiss(notification_float) // 关闭通知悬浮窗4.2 页面过滤与智能显示EasyFloat支持页面过滤功能可以在特定页面自动隐藏悬浮窗EasyFloat.with(applicationContext) .setLayout(R.layout.float_global) .setShowPattern(ShowPattern.ALL_TIME) .setFilter(MainActivity::class.java, LoginActivity::class.java) // 在这些页面不显示 .setTag(global_float) .show()4.3 拖拽吸附与边界限制图EasyFloat支持拖拽边界限制和吸附效果提升用户体验// 设置拖拽边界和吸附效果 EasyFloat.with(this) .setLayout(R.layout.float_draggable) .setDragEnable(true) .setSidePattern(SidePattern.RESULT_SIDE) // 侧边吸附 .setBorder(50, 50, 500, 800) // 设置拖拽边界左、上、右、下 .registerCallback { drag { view, motionEvent - // 实现拖拽关闭功能 DragUtils.registerDragClose(motionEvent, object : OnTouchRangeListener { override fun touchInRange(inRange: Boolean, view: BaseSwitchView) { // 拖拽到删除区域时的反馈 if (inRange) { view.findViewByIdTextView(R.id.tvDelete).text 松手删除 view.findViewByIdImageView(R.id.ivDelete) .setImageResource(R.drawable.icon_delete_selected) } } override fun touchUpInRange() { // 在删除区域松手时关闭悬浮窗 EasyFloat.dismiss() } }) } } .show()4.4 自定义动画与交互效果EasyFloat支持自定义入场和出场动画// 使用自定义动画 EasyFloat.with(this) .setLayout(R.layout.float_animated) .setAnimator(object : OnFloatAnimator { override fun enterAnim(view: View, parent: ViewGroup): Animator { // 自定义入场动画 return ObjectAnimator.ofFloat(view, alpha, 0f, 1f).apply { duration 300 } } override fun exitAnim(view: View, parent: ViewGroup): Animator { // 自定义出场动画 return ObjectAnimator.ofFloat(view, translationY, 0f, 100f).apply { duration 200 } } }) .show() // 或者使用内置的默认动画 .setAnimator(DefaultAnimator())五、实际应用场景案例5.1 音乐播放器悬浮控制面板class MusicPlayerFloat { companion object { private const val TAG music_player_float fun show(context: Context, musicInfo: MusicInfo) { EasyFloat.with(context) .setLayout(R.layout.float_music_player) { view - val ivCover view.findViewByIdImageView(R.id.ivCover) val tvTitle view.findViewByIdTextView(R.id.tvTitle) val tvArtist view.findViewByIdTextView(R.id.tvArtist) val btnPlay view.findViewByIdImageButton(R.id.btnPlay) val btnNext view.findViewByIdImageButton(R.id.btnNext) val btnPrev view.findViewByIdImageButton(R.id.btnPrev) // 加载音乐信息 Glide.with(context).load(musicInfo.coverUrl).into(ivCover) tvTitle.text musicInfo.title tvArtist.text musicInfo.artist // 设置播放控制 btnPlay.setOnClickListener { // 播放/暂停逻辑 MusicPlayer.togglePlay() updatePlayButton(btnPlay) } btnNext.setOnClickListener { MusicPlayer.next() } btnPrev.setOnClickListener { MusicPlayer.previous() } } .setTag(TAG) .setShowPattern(ShowPattern.ALL_TIME) .setDragEnable(true) .setSidePattern(SidePattern.RESULT_HORIZONTAL) .setGravity(Gravity.END or Gravity.BOTTOM, 20, 20) .registerCallback { dragEnd { // 拖拽结束后自动吸附到边缘 it.animate().translationX(0f).setDuration(200).start() } } .show() } fun hide() EasyFloat.hide(TAG) fun dismiss() EasyFloat.dismiss(TAG) } }5.2 全局快捷操作悬浮按钮class QuickActionFloat { companion object { private const val TAG quick_action_float fun init(context: Context) { EasyFloat.with(context.applicationContext) .setLayout(R.layout.float_quick_actions) { view - val btnScreenshot view.findViewByIdButton(R.id.btnScreenshot) val btnRecord view.findViewByIdButton(R.id.btnRecord) val btnNote view.findViewByIdButton(R.id.btnNote) val btnCalculator view.findViewByIdButton(R.id.btnCalculator) btnScreenshot.setOnClickListener { takeScreenshot() } btnRecord.setOnClickListener { startRecording() } btnNote.setOnClickListener { openQuickNote() } btnCalculator.setOnClickListener { showCalculator() } } .setTag(TAG) .setShowPattern(ShowPattern.FOREGROUND) .setDragEnable(true) .setSidePattern(SidePattern.RESULT_VERTICAL) .setLocation(20, 200) .setFilter(SettingsActivity::class.java) // 在设置页面不显示 .registerCallback { createResult { isCreated, msg, _ - if (!isCreated) { Log.e(QuickActionFloat, 创建失败: $msg) } } } .show() } fun toggle() { if (EasyFloat.isShow(TAG)) { EasyFloat.hide(TAG) } else { EasyFloat.show(TAG) } } } }5.3 聊天应用悬浮窗实现class ChatHeadFloat(private val context: Context) { private val TAG chat_head_${System.currentTimeMillis()} fun showForUser(user: User) { EasyFloat.with(context) .setLayout(R.layout.float_chat_head) { view - val ivAvatar view.findViewByIdCircleImageView(R.id.ivAvatar) val tvUnread view.findViewByIdTextView(R.id.tvUnread) val ivOnline view.findViewByIdImageView(R.id.ivOnline) // 加载用户头像 Glide.with(context).load(user.avatar).into(ivAvatar) // 显示未读消息数 val unreadCount getUnreadCount(user.id) if (unreadCount 0) { tvUnread.visibility View.VISIBLE tvUnread.text if (unreadCount 99) 99 else unreadCount.toString() } else { tvUnread.visibility View.GONE } // 在线状态 ivOnline.visibility if (user.isOnline) View.VISIBLE else View.GONE // 点击打开聊天界面 view.setOnClickListener { openChatActivity(user.id) EasyFloat.dismiss(TAG) } // 长按显示更多选项 view.setOnLongClickListener { showChatHeadMenu(user) true } } .setTag(TAG) .setShowPattern(ShowPattern.ALL_TIME) .setDragEnable(true) .setSidePattern(SidePattern.RESULT_BOTH) .setGravity(Gravity.END or Gravity.TOP, 20, 100) .setAnimator(ChatHeadAnimator()) // 自定义聊天头像动画 .registerCallback { drag { view, motionEvent - // 拖拽时显示删除区域 showDeleteZoneIfNeeded(view, motionEvent) } dragEnd { // 拖拽结束后吸附到最近边缘 snapToNearestEdge(it) } } .show() } }六、性能优化与最佳实践6.1 内存管理优化// 1. 及时释放悬浮窗资源 override fun onDestroy() { super.onDestroy() // 在Activity销毁时关闭相关悬浮窗 EasyFloat.dismiss(activity_float) } // 2. 使用弱引用避免内存泄漏 class FloatManager { private val floatWeakRefs mutableMapOfString, WeakReferenceView() fun registerFloatView(tag: String, view: View) { floatWeakRefs[tag] WeakReference(view) } fun getFloatView(tag: String): View? { return floatWeakRefs[tag]?.get() } } // 3. 合理使用页面过滤减少资源消耗 EasyFloat.with(applicationContext) .setShowPattern(ShowPattern.ALL_TIME) .setFilter( SplashActivity::class.java, LoginActivity::class.java, FullscreenVideoActivity::class.java ) // 在这些页面不显示悬浮窗 .show()6.2 用户体验优化// 1. 添加拖拽反馈 .setDragEnable(true) .registerCallback { drag { view, motionEvent - // 拖拽时添加视觉反馈 view.animate().scaleX(1.1f).scaleY(1.1f).setDuration(100).start() // 震动反馈 if (motionEvent.action MotionEvent.ACTION_DOWN) { val vibrator context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator vibrator.vibrate(VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE)) } } dragEnd { // 拖拽结束后恢复原状 it.animate().scaleX(1f).scaleY(1f).setDuration(100).start() } } // 2. 智能显示策略 fun showSmartFloat(context: Context, shouldShow: Boolean) { if (shouldShow !EasyFloat.isShow()) { // 根据设备状态决定显示位置 val display DisplayUtils.getDisplayMetrics(context) val yPosition if (display.heightPixels 2000) 300 else 150 EasyFloat.with(context) .setLayout(R.layout.float_smart) .setGravity(Gravity.END or Gravity.TOP, 20, yPosition) .show() } else if (!shouldShow) { EasyFloat.hide() } }七、常见问题与解决方案7.1 权限相关问题// 权限检查与申请的最佳实践 fun checkAndRequestFloatPermission(activity: Activity, onResult: (Boolean) - Unit) { if (PermissionUtils.checkPermission(activity)) { onResult(true) } else { AlertDialog.Builder(activity) .setTitle(悬浮窗权限申请) .setMessage(使用悬浮窗功能需要您授权悬浮窗权限) .setPositiveButton(去开启) { _, _ - // 自动申请权限 EasyFloat.with(activity) .setLayout(R.layout.float_permission_hint) .setShowPattern(ShowPattern.ALL_TIME) .registerCallback { createResult { isCreated, msg, _ - onResult(isCreated) if (!isCreated) { // 权限申请失败提供手动引导 showManualPermissionGuide(activity) } } } .show() } .setNegativeButton(取消) { _, _ - onResult(false) } .show() } }7.2 多悬浮窗冲突处理// 使用标签系统管理多个悬浮窗 object FloatCoordinator { private val activeFloats mutableSetOfString() fun showFloat(tag: String, config: FloatConfig) { // 检查是否已存在相同标签的悬浮窗 if (activeFloats.contains(tag)) { EasyFloat.dismiss(tag) } // 创建新的悬浮窗 EasyFloat.with(config.context) .applyConfig(config) .setTag(tag) .registerCallback { createResult { isCreated, _, _ - if (isCreated) { activeFloats.add(tag) } } dismiss { activeFloats.remove(tag) } } .show() } fun hideAll() { activeFloats.forEach { tag - EasyFloat.hide(tag) } } fun dismissAll() { activeFloats.forEach { tag - EasyFloat.dismiss(tag) } activeFloats.clear() } }八、总结与展望通过本文的实战案例我们可以看到EasyFloat框架的强大功能和灵活性。无论是简单的提示框还是复杂的交互式悬浮窗EasyFloat都能提供优雅的解决方案。关键优势总结开发效率高链式调用配置简单大幅减少样板代码功能全面支持多种显示模式、拖拽吸附、动画效果等稳定性好完善的权限管理和生命周期控制扩展性强支持自定义View、自定义动画、自定义回调图EasyFloat提供丰富的拖拽交互功能支持拖拽关闭、边界限制等特性未来发展方向支持更多动画效果和交互手势增强多显示器适配能力提供更多预设的悬浮窗模板优化性能减少内存占用EasyFloat让Android悬浮窗开发变得前所未有的简单高效。无论是新手开发者还是经验丰富的工程师都能快速上手并构建出功能丰富的悬浮窗应用。现在就开始使用EasyFloat为你的应用添加炫酷的悬浮窗功能吧项目源码结构参考核心类EasyFloat.kt窗口管理FloatingWindowManager.kt配置类FloatConfig.kt权限管理PermissionUtils.kt示例代码MainActivity.kt【免费下载链接】EasyFloat EasyFloat浮窗从未如此简单Android可拖拽悬浮窗口支持页面过滤、自定义动画可设置单页面浮窗、前台浮窗、全局浮窗浮窗权限按需自动申请...项目地址: https://gitcode.com/gh_mirrors/ea/EasyFloat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…