44
55[ ![ ] ( https://jitpack.io/v/Petterpx/FloatingX.svg )] ( https://jitpack.io/#Petterpx/FloatingX ) [ ![ Scan with Detekt] ( https://github.com/Petterpx/FloatingX/actions/workflows/detekt-analysis.yml/badge.svg )] ( https://github.com/Petterpx/FloatingX/actions/workflows/detekt-analysis.yml ) [ ![ ktlint] ( https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg )] ( https://ktlint.github.io/ )
66
7- ** FloatingX** 一个灵活的 ` 免权限 ` 悬浮窗解决方案。
7+ ** FloatingX** 一个灵活且强大的 ` 免权限 ` 悬浮窗解决方案。
88
99## 👏 特性
1010
11- - 链式调用,无感知插入
12- - 支持滑动,自动吸附,兼容多指触摸
13- - Kotlin-dsl
14- - 自动修复显示位置
15- - 支持对悬浮窗生命周期的监听
16- - 支持自定义悬浮窗各项配置
17- - 支持保存及恢复历史坐标位置
18- - 支持自定义拖动边框,吸附边框
19- - 支持App 内全局悬浮窗,单 ` Activity ` 悬浮窗
20-
21-
22-
23- ## 👨🔧 Loading
24-
25- ** 1.x Todo**
26-
27- - 支持拖动到指定方向显示回收悬浮窗设计
28- - 初始化方式简化
29- - 多个悬浮窗时的自动规避覆盖
30- - 自定义显示隐藏动画效果
31- - 单个 ` ViewGroup ` 悬浮窗
32-
33- ** 2.x Todo**
34-
35- - 悬浮窗方案自动选择
36- - 全局悬浮窗(需要申请权限)
37- - 单App 悬浮窗
38- - 单个 ` Activity ` 悬浮窗
39- - 单个 ` ViewGroup ` 悬浮窗
40-
41-
42-
43- ## 👨💻 使用方式
11+ - 单例持有浮窗view
12+ - 支持各项回调监听
13+ - 链式调用,无感插入
14+ - 支持自定义是否保存历史位置及还原
15+ - 支持插入 ` ViewGroup ` , ` Fragment ` , ` Activity `
16+ - 允许自定义悬浮窗各项指标,自定义隐藏显示动画
17+ - 支持 越界回弹,多指触摸,小屏适配,屏幕旋转
18+ - 支持自定义位置方向,自带辅助定位显示坐标
19+ - 完善的 ` kotlin ` 构建扩展,及对 ` Java ` 的友好兼容
20+ - 支持显示位置[ 强行修复] ,应对特殊机型(需要单独开启)
21+ - 完善的日志系统,打开即可看到不同级别的Fx运行过程,更利于发现问题
22+ - ...
23+
24+ ## 👨💻 依赖方式
4425
4526#### Gradle
4627
@@ -55,158 +36,153 @@ allprojects {
5536
5637``` groovy
5738dependencies {
58- implementation 'com.github.Petterpx:FloatingX:1.0-beta17 '
39+ implementation 'com.github.Petterpx:FloatingX:1.0-rc01 '
5940}
6041```
6142
6243
6344
64- #### 初始化-Application
45+ ## 🏄♀️ 效果图
6546
66- ** Kotlin**
47+ | 全屏,activity,fragment,单view | 小屏展示 | 非正常比例缩放屏幕 |
48+ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
49+ | ![ 效果-展示1] ( https://tva1.sinaimg.cn/large/008i3skNly1gt8cwy2lvrg30700ey1l0.gif ) | ![ 演示-小屏] ( https://tva1.sinaimg.cn/large/008i3skNly1gtb0ruynzpg607c0eie8d02.gif ) | ![ 非正常比例缩放] ( https://tva1.sinaimg.cn/large/008i3skNly1gtb0rxgpgmg607c0ege8702.gif ) |
6750
68- ``` kotlin
69- FloatingX .init {
70- context(this )
71- marginEdge(10f )
72- addBlackClass(MainActivity ::class .java)
73- layout(R .layout.item_floating)
74- defaultDirection(Direction .RIGHT_OR_BOTTOM )
75- }
76- ```
51+ | 屏幕旋转 | 功能演示 | |
52+ | ------------------------------------------------------------ | ------------------------------------------------------------ | ---- |
53+ | ![ 演示-旋转] ( https://tva1.sinaimg.cn/large/008i3skNly1gt8cx7m0wgg30h60fg1kz.gif ) | ![ 演示-局部功能] ( https://tva1.sinaimg.cn/large/008i3skNly1gtayx6t0l0g307c0ege84.gif ) | |
7754
78- ** Java **
55+ ### 完善的日志-查看器
7956
80- ``` java
81- FxHelper config = FxHelper . builder()
82- .context(this )
83- .marginEdge(10f )
84- .layout(R . layout. item_floating)
85- .addBlackClass(MainActivity . class)
86- .build();
87- FloatingX . init(config);
88- ```
57+ 开启日志查看器,将看到Fx整个运行轨迹,更便于发现问题以及追踪解决。同时支持自定义日志tag
8958
90- #### 控制器
59+ | App | Activity | ViewGroup |
60+ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
61+ | ![ image-20210808123000851] (/Users/petterp/Library/Application Support/typora-user-images/image-20210808123000851.png) | ![ image-20210808123414921] ( https://tva1.sinaimg.cn/large/008i3skNly1gt99vralyqj313o0r4jwk.jpg ) | ![ image-20210808123553402] ( https://tva1.sinaimg.cn/large/008i3skNly1gt99xfpfwgj311y0jctc8.jpg ) |
9162
92- ``` kotlin
93- FloatingX .show(Activity ? )
94- FloatingX .hide()
95- FloatingX .dismiss()
96- FloatingX .cancel()
97- FloatingX .clearConfig()
98- ```
99-
100- ** 更多控制**
101-
102- ``` kotlin
103- FloatingX .control() - >
10463
105- @MainThread
106- fun show (activity : Activity )
10764
108- /* * 安装在指定activity上 */
109- fun attach (activity : Activity )
65+ ## 👨🔧 使用方式
11066
111- /* * 安装在指定FrameLayout上 */
112- fun attach (container : FrameLayout )
67+ ### 全局悬浮窗管理
11368
114- /* * 从指定activity上删除 */
115- fun detach (activity : Activity )
69+ ** kt**
11670
117- /* * 从指定FrameLayout上删除 */
118- fun detach (container : FrameLayout )
119-
120- @MainThread
121- fun show ()
71+ ``` kotlin
72+ FloatingX .init {
73+ setContext(this @CustomApplication)
74+ setLayout(R .layout.item_floating_new)
75+ addBlackClass(
76+ MainActivity ::class .java,
77+ NewActivity ::class .java,
78+ ImmersedActivity ::class .java
79+ )
80+ // 只有调用了show,才会监听app-lifecycle,后续会自动插入activity中
81+ show()
82+ }
83+ ```
12284
123- @MainThread
124- fun hide ()
85+ ** Java**
12586
126- /* * 关闭 */
127- @MainThread
128- fun dismiss ()
87+ ``` java
88+ AppHelper helper = AppHelper . builder()
89+ .setContext(application)
90+ .setLayout(R . layout. item_floating)
91+ .build();
92+ FloatingX . init(helper);
93+ ```
12994
130- /* * 获取自定义的view */
131- fun getView (): View ?
13295
133- /* * 当前是否显示 */
134- fun isShowRunning (): Boolean
13596
136- /* * 更新params */
137- @MainThread
138- fun updateParams (params : ViewGroup .LayoutParams )
97+ ### 局部悬浮窗管理
13998
140- /* * 提供一个回调入口,用于快捷刷新 */
141- @MainThread
142- fun updateView (obj : (FxViewHolder ) -> Unit )
99+ #### 通用创建方式
143100
144- /* * 更新当前view */
145- @MainThread
146- fun updateView (@LayoutRes resource : Int )
101+ ** kt**
147102
148- fun setClickListener (obj : (View ) -> Unit )
103+ ``` kotlin
104+ ScopeHelper .builder {
105+ setLayout(R .layout.item_floating)
106+ }.toControl(activity)
149107```
150108
109+ ** kt && java**
151110
111+ ``` kotlin
112+ ScopeHelper .builder()
113+ .setLayout(R .layout.item_floating)
114+ .build()
115+ .toControl(activity)
116+ .toControl(fragment)
117+ .toControl(viewgroup)
118+ ```
152119
153- #### 进阶使用
120+ #### 对kt的扩展支持
154121
155- wiki ** loading **
122+ ##### activity创建悬浮窗
156123
157- ---
124+ ``` kotlin
125+ private val activityFx by activityToFx(activity) {
126+ setLayout(R .layout.item_floating)
127+ }
128+ ```
158129
130+ ##### fragment创建悬浮窗
159131
132+ ``` kotlin
133+ private val fragment by fragmentToFx(fragment) {
134+ setLayout(R .layout.item_floating)
135+ }
136+ ```
160137
161- ## 🚴♀️ 业务决定方案
138+ ##### viewGroup创建悬浮窗
162139
163- 对于悬浮窗方案,我们常见有两种实现方式:
140+ ``` kotlin
141+ private val viewFx by createFx({
142+ init (viewGroup)
143+ }) {
144+ setLayout(R .layout.item_floating)
145+ setEnableLog(true , " main_fx" )
146+ }
147+ ```
164148
165- - 前者是基于 ` WindowManager ` ,从而不依赖 ` Activity ` ,实现添加 ` View ` ,达到悬浮窗的目的。
166- - 后者基于 ` Activity-DecorView ` 动态添加移除 ` view ` ,从而达到悬浮窗目的。
149+ ##### 快速创建任意作用域悬浮窗
167150
168- #### 🖐 综合对比:
151+ ``` kotlin
152+ private val customCreateFx by createFx {
153+ setLayout(R .layout.item_floating)
154+ build().toControl(activity)
155+ build().toControl(fragment)
156+ build().toControl(viewgroup)
157+ }
158+ ```
169159
170- | 实现方案 | app内显示 | app外显示 | 头部app方案 | 兼容性 | 显示效果 | 权限请求 |
171- | :----------------: | :-------: | :-------: | :---------: | :-----: | :------: | :------: |
172- | WindowManager | ✅ | ✅ | ✅ | ✅ | ✅ ✅ ✅ | ✅ (8.0+) |
173- | Activity-DecorView | ✅ | ❎ | ✅ | ✅ ✅ ✅ | ✅ | ❎ |
160+ ## 🤔 技术实现
174161
175- > ✅ 代表支持 、 ✅ ✅ 效果好 、 ✅ ✅ ✅ 效果极佳
162+ > ** App ** 级别悬浮窗 基于 ` DecorView ` 的的实现方案,全局持有一个单独的悬浮窗 ` View ` ,通过 ` AppLifecycle ` 监听 ` Activity ` 生命周期,并在相应时机 插入到 ` DecorView ` 上 ;
176163>
177- > ❎ 代表 不需要或者不支持。
178-
179-
180-
181- 如果app需要 ** app外显示悬浮窗** ,及是 ** 游戏sdk** 相关,以及需要支持 ** 语音通话** 这种,那么 ` WMS ` 这种方案是最好。
182-
183- 如果app ** 不需要app外显示** ,不想申请权限,对显示效果可接受(接受 ` Activity ` 切换时悬浮窗的闪动,当然也有补救措施),那么后者方案适合你。
184-
185- 头部app采用悬浮窗的:
186-
187- ** 微信** ,QQ,采用的基本都是 ` WMS ` 的方案(很大程度是因为音视频),** 知乎** 采用的是免权限。
188-
189-
190-
191- ---
192-
193- ## 🐬 技术实现
194-
195- > 基于 ` DecorView ` 的的实现方案,全局持有一个单独的悬浮窗 ` View ` ,通过 ` AppLifecycle ` 监听 ` Activity ` 生命周期,并在相应时机 插入到 ` DecorView ` 上 。
164+ > ** View** 级别悬浮窗,基于给定的 ` ViewGroup ` ;
165+ >
166+ > ** Fragment** 级别,基于其对应的 ` rootView ` ;
167+ >
168+ > ** Acrtivity** 级别,基于 ` DecorView ` 内部的 ` R.id.content ` ;
196169
197170具体如下:
198171
199172<img src =" https://tva1.sinaimg.cn/large/008i3skNly1gr20ks7780j30rc0i5dim.jpg " alt =" Activity-setContentView " />
200173
201- 具体参见我的博客 :[ 源码分析 | Activity-setContentView] ( https://juejin.cn/post/6897453195342610445 )
174+ 具体见我的博客 :[ 源码分析 | Activity-setContentView] ( https://juejin.cn/post/6897453195342610445 )
202175
203- Ps: 为什么要插入到 ` DecorView ` ,而不是 ** R.id.content** -> ` FrameLayout ` ?
176+ Ps: 为什么App级别悬浮窗 要插入到 ` DecorView ` ,而不是 ** R.id.content** -> ` FrameLayout ` ?
204177
205178> 插入到 ` DecorView ` 可以最大程度控制悬浮窗的自由度,即悬浮窗可以真正意义上[ ` 全屏 ` ] 拖动。
206179>
207180> 插入到 ` content ` 中,其拖动范围其实为 ** 应用视图范围** ,即摆放位置 受到 ** 状态栏** 和 ** 底部导航栏** 以及 默认的 ` AppBar ` 影响, 比如当用户隐藏了状态栏或者导航栏,相对应的视图大小会发生改变,将影响悬浮窗的位置摆放。
208181
182+
183+
209184## 👍 感谢
210185
211- 基础 ** 悬浮窗View** 源自 [ EnFloatingView] ( https://github.com/leotyndale/EnFloatingView ) 的 [ FloatingMagnetView] ( https://github.com/leotyndale/EnFloatingView/blob/master/floatingview/src/main/java/com/imuxuan/floatingview/FloatingMagnetView.java ) 实现方式,并在其基础上增加了一些改进!
186+ 基础 ** 悬浮窗View** 源自 [ EnFloatingView] ( https://github.com/leotyndale/EnFloatingView ) 的 [ FloatingMagnetView] ( https://github.com/leotyndale/EnFloatingView/blob/master/floatingview/src/main/java/com/imuxuan/floatingview/FloatingMagnetView.java ) 实现方式,并在其基础上增加了一些改进。
212187
188+ 对于导航栏的测量部分代码来自,wenlu,并在其之上增加了更多适配,已覆盖市场95%机型,可以说是目前能搜到的唯一可以准确测量的工具。
0 commit comments