Skip to content

Commit

Permalink
动态适配虚拟导航栏
Browse files Browse the repository at this point in the history
  • Loading branch information
princekin-f committed Feb 16, 2020
1 parent 3cbf87f commit c2c5857
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 68 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ allprojects {
- **在应用模块的`build.gradle`添加:**
```
dependencies {
implementation 'com.github.princekin-f:EasyFloat:1.2.9'
implementation 'com.github.princekin-f:EasyFloat:1.3.0'
}
```

Expand Down Expand Up @@ -80,7 +80,7 @@ EasyFloat.with(this)
.setSidePattern(SidePattern.RESULT_HORIZONTAL)
// 设置浮窗的标签,用于区分多个浮窗
.setTag("testFloat")
// 设置浮窗是否可拖拽
// 设置浮窗是否可拖拽,默认可拖拽
.setDragEnable(true)
// 系统浮窗是否包含EditText,仅针对系统浮窗,默认不包含
.hasEditText(false)
Expand All @@ -96,6 +96,8 @@ EasyFloat.with(this)
.setAppFloatAnimator(AppFloatDefaultAnimator())
// 设置系统浮窗的不需要显示的页面
.setFilter(MainActivity::class.java, SecondActivity::class.java)
// 设置系统浮窗的有效显示高度(不包含虚拟导航栏的高度),基本用不到,除非有虚拟导航栏适配问题
.setDisplayHeight(OnDisplayHeight { context -> DisplayUtils.rejectedNavHeight(context) })
// 浮窗的一些状态回调,如:创建结果、显示、隐藏、销毁、touchEvent、拖拽过程、拖拽结束。
// ps:通过Kotlin DSL实现的回调,可以按需复写方法,用到哪个写哪个
.registerCallback {
Expand Down
3 changes: 3 additions & 0 deletions UpdateDoc.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
## 版本更新日志
#### v 1.3.0:
- 可动态适配虚拟导航栏:`setDisplayHeight(displayHeight: OnDisplayHeight)`

#### v 1.2.9:
- [优化细节。](https://github.com/princekin-f/EasyFloat/issues/57)

Expand Down
52 changes: 28 additions & 24 deletions easyfloat/src/main/java/com/lzf/easyfloat/EasyFloat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ class EasyFloat {
*/
@JvmStatic
@JvmOverloads
fun getFloatView(activity: Activity? = null, tag: String? = null): View? =
fun getFloatView(activity: Activity? = null, tag: String? = null) =
manager(activity)?.getFloatView(tag)

/**
* 获取Activity浮窗管理类
*/
private fun manager(activity: Activity?): ActivityFloatManager? {
val a: Activity? = activity ?: activityWr?.get()
return if (a != null) ActivityFloatManager(a) else null
return a?.run { ActivityFloatManager(this) }
}

// *************************** 以下系统浮窗的相关方法 ***************************
Expand Down Expand Up @@ -170,45 +170,42 @@ class EasyFloat {
// 创建浮窗数据类,方便管理配置
private val config = FloatConfig()

fun setSidePattern(sidePattern: SidePattern): Builder =
fun setSidePattern(sidePattern: SidePattern) =
this.apply { config.sidePattern = sidePattern }

fun setShowPattern(showPattern: ShowPattern): Builder =
fun setShowPattern(showPattern: ShowPattern) =
this.apply { config.showPattern = showPattern }

@JvmOverloads
fun setLayout(layoutId: Int, invokeView: OnInvokeView? = null): Builder = this.apply {
fun setLayout(layoutId: Int, invokeView: OnInvokeView? = null) = this.apply {
config.layoutId = layoutId
config.invokeView = invokeView
}

@JvmOverloads
fun setGravity(gravity: Int, offsetX: Int = 0, offsetY: Int = 0): Builder = this.apply {
fun setGravity(gravity: Int, offsetX: Int = 0, offsetY: Int = 0) = this.apply {
config.gravity = gravity
config.offsetPair = Pair(offsetX, offsetY)
}

fun setLocation(x: Int, y: Int): Builder = this.apply { config.locationPair = Pair(x, y) }
fun setLocation(x: Int, y: Int) = this.apply { config.locationPair = Pair(x, y) }

fun setTag(floatTag: String?): Builder = this.apply { config.floatTag = floatTag }
fun setTag(floatTag: String?) = this.apply { config.floatTag = floatTag }

fun setDragEnable(dragEnable: Boolean): Builder =
this.apply { config.dragEnable = dragEnable }
fun setDragEnable(dragEnable: Boolean) = this.apply { config.dragEnable = dragEnable }

/**
* 该方法针对系统浮窗,单页面浮窗无需设置
*/
fun hasEditText(hasEditText: Boolean): Builder =
this.apply { config.hasEditText = hasEditText }
fun hasEditText(hasEditText: Boolean) = this.apply { config.hasEditText = hasEditText }

@Deprecated("建议直接在 setLayout 设置详细布局")
fun invokeView(invokeView: OnInvokeView): Builder =
this.apply { config.invokeView = invokeView }
fun invokeView(invokeView: OnInvokeView) = this.apply { config.invokeView = invokeView }

/**
* 通过传统接口,进行浮窗的各种状态回调
*/
fun registerCallbacks(callbacks: OnFloatCallbacks): Builder =
fun registerCallbacks(callbacks: OnFloatCallbacks) =
this.apply { config.callbacks = callbacks }

/**
Expand All @@ -219,20 +216,27 @@ class EasyFloat {
config.floatCallbacks = FloatCallbacks().apply { registerListener(builder) }
}

fun setAnimator(floatAnimator: OnFloatAnimator?): Builder =
fun setAnimator(floatAnimator: OnFloatAnimator?) =
this.apply { config.floatAnimator = floatAnimator }

fun setAppFloatAnimator(appFloatAnimator: OnAppFloatAnimator?): Builder =
fun setAppFloatAnimator(appFloatAnimator: OnAppFloatAnimator?) =
this.apply { config.appFloatAnimator = appFloatAnimator }

fun setMatchParent(widthMatch: Boolean = false, heightMatch: Boolean = false): Builder =
this.apply {
config.widthMatch = widthMatch
config.heightMatch = heightMatch
}
/**
* 设置屏幕的有效显示高度(不包含虚拟导航栏的高度)
*/
fun setDisplayHeight(displayHeight: OnDisplayHeight) =
this.apply { config.displayHeight = displayHeight }

// 设置需要过滤的Activity,仅对系统浮窗有效
fun setFilter(vararg clazz: Class<*>): Builder = this.apply {
fun setMatchParent(widthMatch: Boolean = false, heightMatch: Boolean = false) = this.apply {
config.widthMatch = widthMatch
config.heightMatch = heightMatch
}

/**
* 设置需要过滤的Activity,仅对系统浮窗有效
*/
fun setFilter(vararg clazz: Class<*>) = this.apply {
clazz.forEach {
config.filterSet.add(it.name)
// 过滤掉当前Activity
Expand Down
10 changes: 5 additions & 5 deletions easyfloat/src/main/java/com/lzf/easyfloat/data/FloatConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ import com.lzf.easyfloat.anim.AppFloatDefaultAnimator
import com.lzf.easyfloat.anim.DefaultAnimator
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern
import com.lzf.easyfloat.interfaces.OnAppFloatAnimator
import com.lzf.easyfloat.interfaces.OnFloatAnimator
import com.lzf.easyfloat.interfaces.OnFloatCallbacks
import com.lzf.easyfloat.interfaces.OnInvokeView
import com.lzf.easyfloat.interfaces.FloatCallbacks
import com.lzf.easyfloat.interfaces.*
import com.lzf.easyfloat.utils.DefaultDisplayHeight

/**
* @author: liuzhenfeng
Expand Down Expand Up @@ -64,6 +61,9 @@ data class FloatConfig(
var floatAnimator: OnFloatAnimator? = DefaultAnimator(),
var appFloatAnimator: OnAppFloatAnimator? = AppFloatDefaultAnimator(),

// 设置屏幕的有效显示高度(不包含虚拟导航栏的高度),仅针对系统浮窗,一般不用复写
var displayHeight: OnDisplayHeight = DefaultDisplayHeight(),

// 不需要显示系统浮窗的页面集合,参数为类名
val filterSet: MutableSet<String> = mutableSetOf(),
// 是否设置,当前创建的页面也被过滤
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.lzf.easyfloat.interfaces;

import android.content.Context;

import org.jetbrains.annotations.NotNull;

/**
* @author: liuzhenfeng
* @function: 通过接口获取屏幕的有效显示高度
* @date: 2020-02-16 16:21
*/
public interface OnDisplayHeight {

/**
* 获取屏幕有效的显示高度,不包含虚拟导航栏
*
* @param context ApplicationContext
* @return 高度值(int类型)
*/
int getDisplayRealHeight(@NotNull Context context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.lzf.easyfloat.utils

import android.content.Context
import com.lzf.easyfloat.interfaces.OnDisplayHeight

/**
* @author: liuzhenfeng
* @function: 获取屏幕有效高度的实现类
* @date: 2020-02-16 16:26
*/
internal class DefaultDisplayHeight : OnDisplayHeight {

override fun getDisplayRealHeight(context: Context) = DisplayUtils.rejectedNavHeight(context)

}
51 changes: 15 additions & 36 deletions easyfloat/src/main/java/com/lzf/easyfloat/utils/DisplayUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import android.util.Log
import android.view.*
import com.lzf.easyfloat.permission.rom.RomUtils


/**
* @author: liuzhenfeng
* @function:
* @function: 屏幕显示相关工具类
* @date: 2019-05-23 15:23
*/
object DisplayUtils {
Expand Down Expand Up @@ -90,19 +89,19 @@ object DisplayUtils {
* 获取导航栏当前的高度
*/
fun getNavigationBarCurrentHeight(context: Context) =
if (isNavigationBarShow(context)) getNavigationBarHeight(context) else 0

if (hasNavigationBar(context)) getNavigationBarHeight(context) else 0

/**
* 判断虚拟导航栏是否显示
*
* @param context 上下文对象
* @return true(显示虚拟导航栏),false(不显示或不支持虚拟导航栏)
*/
fun isNavigationBarShow(context: Context) = when {
RomUtils.checkIsHuaweiRom() -> huaweiHasNavigationBar(context)
RomUtils.checkIsMiuiRom() -> !isMiuiFullScreen(context)
RomUtils.checkIsVivoRom() -> !isVivoFullScreen(context)
fun hasNavigationBar(context: Context) = when {
getNavigationBarHeight(context) == 0 -> false
RomUtils.checkIsHuaweiRom() && isHuaWeiHideNav(context) -> false
RomUtils.checkIsMiuiRom() && isMiuiFullScreen(context) -> false
RomUtils.checkIsVivoRom() && isVivoFullScreen(context) -> false
else -> isHasNavigationBar(context)
}

Expand All @@ -116,35 +115,15 @@ object DisplayUtils {
}

/**
* 华为手机是否显示 NavigationBar
* 华为手机是否隐藏了虚拟导航栏
* @return true 表示隐藏了,false 表示未隐藏
*/
private fun huaweiHasNavigationBar(context: Context): Boolean {
var hasNavigationBar = false
val rs = context.resources
val id = rs.getIdentifier("config_showNavigationBar", "bool", "android")
if (id > 0) hasNavigationBar = rs.getBoolean(id)

try {
val systemPropertiesClass = Class.forName("android.os.SystemProperties")
val m = systemPropertiesClass.getMethod("get", String::class.java)
val navBarOverride =
m.invoke(systemPropertiesClass, "qemu.hw.mainkeys") as String
// 判断是否隐藏了底部虚拟导航
val navigationBarIsMin =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Settings.System.getInt(context.contentResolver, "navigationbar_is_min", 0)
} else {
Settings.Global.getInt(context.contentResolver, "navigationbar_is_min", 0)
}
if ("1" == navBarOverride || 1 == navigationBarIsMin) {
hasNavigationBar = false
} else if ("0" == navBarOverride) {
hasNavigationBar = true
}
} catch (e: Exception) {
}
return hasNavigationBar
}
private fun isHuaWeiHideNav(context: Context) =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Settings.System.getInt(context.contentResolver, "navigationbar_is_min", 0)
} else {
Settings.Global.getInt(context.contentResolver, "navigationbar_is_min", 0)
} != 0

/**
* 小米手机是否开启手势操作
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ internal class TouchUtils(val context: Context, val config: FloatConfig) {
lastY = event.rawY
// 屏幕宽高需要每次获取,可能会有屏幕旋转、虚拟导航栏的状态变化
parentWidth = DisplayUtils.getScreenWidth(context)
parentHeight = DisplayUtils.rejectedNavHeight(context)
parentHeight = config.displayHeight.getDisplayRealHeight(context)
// 获取在整个屏幕内的绝对坐标
view.getLocationOnScreen(location)
// 通过绝对高度和相对高度比较,判断包含顶部状态栏
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.lzf.easyfloat.example.logger;
import com.lzf.easyfloat.interfaces.OnFloatCallbacks;
import com.lzf.easyfloat.permission.PermissionUtils;
import com.lzf.easyfloat.utils.DisplayUtils;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -94,6 +95,8 @@ private void test() {
.setAppFloatAnimator(new AppFloatDefaultAnimator())
// 设置系统浮窗的不需要显示的页面
.setFilter(MainActivity.class, SecondActivity.class)
// 设置系统浮窗的有效显示高度(不包含虚拟导航栏的高度),基本用不到,除非有虚拟导航栏适配问题
.setDisplayHeight(DisplayUtils.INSTANCE::rejectedNavHeight)
// 浮窗的一些状态回调,如:创建结果、显示、隐藏、销毁、touchEvent、拖拽过程、拖拽结束。
.registerCallbacks(new OnFloatCallbacks() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ import com.lzf.easyfloat.anim.DefaultAnimator
import com.lzf.easyfloat.enums.ShowPattern
import com.lzf.easyfloat.enums.SidePattern
import com.lzf.easyfloat.example.R
import com.lzf.easyfloat.interfaces.OnDisplayHeight
import com.lzf.easyfloat.interfaces.OnFloatCallbacks
import com.lzf.easyfloat.interfaces.OnInvokeView
import com.lzf.easyfloat.utils.DisplayUtils
import com.lzf.easyfloat.utils.InputMethodUtils
import kotlinx.android.synthetic.main.activity_third.*

Expand Down Expand Up @@ -109,6 +111,8 @@ class ThirdActivity : Activity() {
.setAppFloatAnimator(AppFloatDefaultAnimator())
// 设置系统浮窗的不需要显示的页面
.setFilter(MainActivity::class.java, SecondActivity::class.java)
// 设置系统浮窗的有效显示高度(不包含虚拟导航栏的高度),基本用不到,除非有虚拟导航栏适配问题
.setDisplayHeight(OnDisplayHeight { context -> DisplayUtils.rejectedNavHeight(context) })
// 浮窗的一些状态回调,如:创建结果、显示、隐藏、销毁、touchEvent、拖拽过程、拖拽结束。
// ps:通过Kotlin DSL实现的回调,可以按需复写方法,用到哪个写哪个
.registerCallback {
Expand Down

0 comments on commit c2c5857

Please sign in to comment.