Skip to content

Commit 3a40fbb

Browse files
authored
Merge pull request #23 from Petterpx/pref_fx_basis
doc:update
2 parents 64703e0 + 8b5874d commit 3a40fbb

1 file changed

Lines changed: 112 additions & 136 deletions

File tree

README.md

Lines changed: 112 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,24 @@
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
5738
dependencies {
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

Comments
 (0)