继续创造,加快成长!这是我参加「日新计划 10 月更文挑战」的第2天,点击查看活动详情
前言
首先在文章开始之前先抛出几个问题,让咱们带着疑问往下走:
什么是窗口操控?
在Android手机中状态栏,导航栏,输入法等这些与app无关,可是需求合作app一同运用的窗口部件。
之前咱们都是怎么办理窗口的?
在window上增加各种flag,有些flag只适应于指定的版别,而某些flag在高版别不能生效,清除flag也相对费事。
WindowInsetsController 又能处理什么问题?
WindowInsetsController 的推出是来取代之前杂乱费事的窗口操控,之前增加各种Flag不容易了解,而运用Api的办法来办理窗口,更加的语义化,更加的便利了解,能够说看到Api办法就知道是什么意思,运用起来却是很便利。
WindowInsetsController 就真的没有兼容性问题吗?
尽管flag这欠好那欠好,那咱们直接用 WindowInsetsController 就能够了吗?可是 WindowInsetsController 需求Android 11 (R) API 30 才干运用。尽管谷歌又推出了 ViewCompat 的Api 向下兼容到5.0版别,可是5.0以下的版别怎么办?
可能现在的一些新运用都是5.0以上了,可是这个兼容到哪一个版别也并不是咱们开发者说了算,万一要兼容5.0一下怎么办?
就算咱们的运用是支撑5.0以上,那么咱们运用 WindowInsetsController 与 windowInsets 就能够了吗?并不是!
就算是 WindowInsetsController 或它的兼容包 WindowInsetsControllerCompat 也并不是悉数就能用的,也会有兼容性问题。部分设备不能用,部分版别不能用等等。
说了这么多,到底怎么运用?下面一同来看看吧!
一、WindowInsetsController 与 windowInsets 的运用
WindowInsetsController 能办理的东西不少,可是咱们常用的便是状态栏,导航栏,软键盘的一些办理,下面咱们就基于这几点来看看到底怎么操控
1.1 状态栏
榜首种办法:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.decorView.setOnApplyWindowInsetsListener { view: View, windowInsets: WindowInsets ->
//状态栏
val statusBars = windowInsets.getInsets(WindowInsets.Type.statusBars())
//状态栏高度
val statusBarHeight = Math.abs(statusBars.bottom - statusBars.top)
windowInsets
}
}
第二种办法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowInsets = window.decorView.rootWindowInsets
//状态栏
val statusBars = windowInsets.getInsets(WindowInsets.Type.statusBars())
//状态栏高度
val statusBarHeight = Math.abs(statusBars.bottom - statusBars.top)
YYLogUtils.w("statusBarHeight2:$statusBarHeight")
}
第三种办法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.decorView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(view: View?) {
val windowInsets = window.decorView.rootWindowInsets
//状态栏
val statusBars = windowInsets.getInsets(WindowInsets.Type.statusBars())
//状态栏高度
val statusBarHeight = Math.abs(statusBars.bottom - statusBars.top)
YYLogUtils.w("statusBarHeight2:$statusBarHeight")
}
override fun onViewDetachedFromWindow(view: View?) {
}
})
}
榜首种办法和第三种办法是运用监听回调的办法获取到状态栏高度,第二种办法是运用同步的办法获取状态栏高度,可是第二种办法有坑,它无法在 onCreate 中运用,直接运用会空指针的。
为什么?其实也能了解,onCreate 办法其实便是解析布局增加布局,并没有展现出来,所以咱们第三种办法运用了监听,当View现已 OnAttach 之后咱们再调用办法才干运用。
1.2 导航栏
榜首种办法:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.decorView.setOnApplyWindowInsetsListener { view: View, windowInsets: WindowInsets ->
//导航栏
val statusBars = windowInsets.getInsets(WindowInsets.Type.statusBars())
//导航栏高度
val navigationHeight = Math.abs(statusBars.bottom - statusBars.top)
YYLogUtils.w("navigationHeight:$navigationHeight")
windowInsets
}
}
第二种办法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val windowInsets = window.decorView.rootWindowInsets
//导航栏
val statusBars = windowInsets.getInsets(WindowInsets.Type.statusBars())
//导航栏高度
val navigationHeight = Math.abs(statusBars.bottom - statusBars.top)
YYLogUtils.w("navigationHeight:$navigationHeight")
YYLogUtils.w("statusBarHeight2:$statusBarHeight")
}
第三种办法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.decorView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(view: View?) {
val windowInsets = window.decorView.rootWindowInsets
//导航栏
val statusBars = windowInsets.getInsets(WindowInsets.Type.statusBars())
//导航栏高度
val navigationHeight = Math.abs(statusBars.bottom - statusBars.top)
YYLogUtils.w("navigationHeight:$navigationHeight")
}
override fun onViewDetachedFromWindow(view: View?) {
}
})
}
其实导航栏和状态栏是一样样的,这儿打印Log如下:
能够看到其实也更推荐咱们运用第三种办法,因为它是在 onAttach 中调用,而其他的办法需求在 onResume 之后调用,相对来说第三种办法更快一些。
1.3 软键盘
相同的咱们能够操作软键盘的翻开,收起,还能监听软键盘弹起的动画的Value,获取当时的值,这个也是巨便利
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
//翻开键盘
window?.insetsController?.show(WindowInsets.Type.ime())
// mBinding.llRoot.windowInsetsController?.show(WindowInsets.Type.ime())
window.decorView.setWindowInsetsAnimationCallback(object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
override fun onProgress(insets: WindowInsets, runningAnimations: MutableList<WindowInsetsAnimation>): WindowInsets {
val isVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val keyboardHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
//当时是否展现
YYLogUtils.w("isVisible = $isVisible")
//当时的高度进展回调
YYLogUtils.w("keyboardHeight = $keyboardHeight")
return insets
}
})
}
咱们能够经过 window?.insetsController
或者 window.decorView.windowInsetsController?
来获取 WindowInsetsController 对象,经过 Controller 对象咱们就能操作软键盘了。
打印Log如下:
封闭软键盘:
翻开软键盘:
1.4 其他
除了软键盘的操作,咱们还能进行其他的操作
window?.insetsController?.apply {
show(WindowInsetsCompat.Type.ime())
show(WindowInsetsCompat.Type.statusBars())
show(WindowInsetsCompat.Type.navigationBars())
show(WindowInsetsCompat.Type.systemBars())
}
不过都不是太常用。
除此之外咱们还能设置状态栏与导航栏的文本图标色彩
window?.insetsController?.apply {
setAppearanceLightNavigationBars(true)
setAppearanceLightStatusBars(false)
}
不过也并欠好用,内部有兼容性问题。
二、兼容库 WindowInsetsControllerCompat 的运用
为了兼容低版别的Android,咱们能够运用
implementation 'androidx.core:core:1.5.0'
以上的版别,内部即可运用 WindowInsetsControllerCompat 兼容库,最多能够支撑到5.0以上版别。
这儿我运用的是
implementation 'androidx.core:core:1.6.0'
版别作为示例。
2.1 状态栏
咱们关于前面的版别,相同的咱们运用三种办法来获取
办法一:
ViewCompat.setOnApplyWindowInsetsListener(view, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
Insets statusInsets = insets.getInsets(WindowInsetsCompat.Type.statusBars());
int top = statusInsets.top;
int bottom = statusInsets.bottom;
int height = Math.abs(bottom - top);
return insets;
}
});
办法二:
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(view);
assert windowInsets != null;
int top = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).top;
int bottom = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).bottom;
int height = Math.abs(bottom - top);
办法三:
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(v);
assert windowInsets != null;
int top = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).top;
int bottom = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).bottom;
int height = Math.abs(bottom - top);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
和R的版别共同,我更推荐运用第三种办法,当View现已 OnAttach 之后咱们再调用办法,更方便一点。
2.2 导航栏
办法一:
ViewCompat.setOnApplyWindowInsetsListener(view, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
Insets navInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
int top = navInsets.top;
int bottom = navInsets.bottom;
int height = Math.abs(bottom - top);
return insets;
}
});
办法二:
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(view);
assert windowInsets != null;
int top = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).top;
int bottom = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
int height = Math.abs(bottom - top);
办法三:
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(v);
assert windowInsets != null;
int top = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).top;
int bottom = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
int height = Math.abs(bottom - top);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
和R版别的共同,这样即可正确的获取到底部导航栏的高度
2.3 软键盘
操作软键盘的办法和R的版别差不多,仅仅调用的类变成了兼容类。
ViewCompat.setWindowInsetsAnimationCallback(window.decorView, object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
override fun onProgress(insets: WindowInsetsCompat, runningAnimations: MutableList<WindowInsetsAnimationCompat>): WindowInsetsCompat {
val isVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val keyboardHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
//当时是否展现
YYLogUtils.w("isVisible = $isVisible")
//当时的高度进展回调
YYLogUtils.w("keyboardHeight = $keyboardHeight")
return insets
}
})
ViewCompat.getWindowInsetsController(findViewById(android.R.id.content))?.apply {
show(WindowInsetsCompat.Type.ime())
}
这样的兼容类,其实并没有彻底兼容,低版别的部分手机还是拿不到进展。
那么咱们能够在兼容类上再做一个版别的兼容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
activity.window.decorView.setWindowInsetsAnimationCallback(object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
override fun onProgress(insets: WindowInsets, animations: MutableList<WindowInsetsAnimation>): WindowInsets {
val imeHeight = insets.getInsets(WindowInsets.Type.ime()).bottom
listener.onKeyboardHeightChanged(imeHeight)
return insets
}
})
} else {
ViewCompat.setOnApplyWindowInsetsListener(activity.window.decorView) { _, insets ->
val posBottom = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
listener.onKeyboardHeightChanged(posBottom)
insets
}
}
无赖,兼容类的软键盘监听作用并欠好,只能运用以前的办法。
打印的Log如下:
2.4 其他
相同的咱们能够运用兼容类来操作状态栏,导航栏,软键盘等
View decorView = activity.findViewById(android.R.id.content);
WindowInsetsControllerCompat controller = ViewCompat.getWindowInsetsController(decorView);
if (controller != null) {
controller.show(WindowInsetsCompat.Type.navigationBars());
controller.show(WindowInsetsCompat.Type.statusBars());
controller.show(WindowInsetsCompat.Type.ime());
}
留意坑点,假如运用的是Activity对象,这儿推荐运用 findViewById(android.R.id.content) 的办法来获取View来操作,假如是经过window.decorView 来获取 Controller 有可能为null。
操控导航栏,状态栏的文本图标色彩
WindowInsetsControllerCompat controller = ViewCompat.getWindowInsetsController(activity.findViewById(android.R.id.content));
if (controller != null) {
controller.setAppearanceLightNavigationBars(false);
controller.setAppearanceLightStatusBars(false);
}
留意坑点,看起来很夸姣,其实底部导航栏只要版别R以上才干操控,而顶部状态栏的色彩操控则有很大的兼容性问题,简直不可用,我目前测试过的机型只要一款能生效。
三、实战中兼容库的兼容问题
在运用的开发中咱们能够用 WindowInsetsControllerCompat 吗?它能处理咱们那些痛点呢?
当然能够用,在状态栏高度,导航栏高度,判别状态栏导航栏是否显现,监听软键盘的高度等一系列场景中的确能起到很好的作用。
为什么要用 WindowInsetsControllerCompat ?
看之前的状态栏高度,导航栏高度获取,都是监听的办法获取啊,假如想运用我还需求加个回调才行,这儿就引进一个问题,一定要异步运用吗?运用同步行不行?
博主,你这个太杂乱了,咱们之前的办法都是直接一个静态办法就行了。
/**
* 老的办法获取状态栏高度
*/
private static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
/**
* 老的办法获取导航栏的高度
*/
private static int getNavigationBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
相信咱们包括我都是这么用的,的确简单好用有快速又便捷,搞得这些监听啊回调啊有个diao用?
可是可是,这些值仅仅预设的值,部分手机厂商会修正不运用这些值,而咱们运用 WindowInsets 的办法来获取的话,是其真实展现的值。
例如状态栏的高度,早前一些刘海屏的手机,假如刘海做的比较大,比较高,状态栏的高度都显现不下,那么就会加大状态栏高度,那么运用预设值就会有问题,显得比较小。
再比方现在盛行的全面屏手机,全面屏手势,因为要兼容各种操作形式,底部的导航栏高度就彻底不是预设值,假如还是用老办法就会踩大坑了。
如下图,非常典型的比方,真实的导航栏是黑色,运用老办法获取到的导航栏高度为深灰色。
再比方判别导航栏是否存在,因为部分手机能够手动躲藏导航栏,还能在设置中动态改变交互形式,全面屏手势,底部三大金刚键等。
咱们运用的老的办法,大约都是这样判别:
/**
* 老办法,并欠好用
*/
public static boolean isNavBarVisible(Context context) {
boolean isVisible = false;
if (!(context instanceof Activity)) {
return false;
}
Activity activity = (Activity) context;
Window window = activity.getWindow();
ViewGroup decorView = (ViewGroup) window.getDecorView();
for (int i = 0, count = decorView.getChildCount(); i < count; i++) {
final View child = decorView.getChildAt(i);
final int id = child.getId();
if (id != View.NO_ID) {
String resourceEntryName = context.getResources().getResourceEntryName(id);
if ("navigationBarBackground".equals(resourceEntryName) && child.getVisibility() == View.VISIBLE) {
isVisible = true;
break;
}
}
}
if (isVisible) {
// 关于三星手机,android10以下做单独的判别
if (isSamsung()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
try {
return Settings.Global.getInt(activity.getContentResolver(), "navigationbar_hide_bar_enabled") == 0;
} catch (Exception ignore) {
}
}
int visibility = decorView.getSystemUiVisibility();
isVisible = (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
}
return isVisible;
}
private static final String[] ROM_SAMSUNG = {"samsung"};
private static boolean isSamsung() {
final String brand = getBrand();
final String manufacturer = getManufacturer();
return isRightRom(brand, manufacturer, ROM_SAMSUNG);
}
private static String getBrand() {
try {
String brand = Build.BRAND;
if (!TextUtils.isEmpty(brand)) {
return brand.toLowerCase();
}
} catch (Throwable ignore) {/**/}
return "UNKNOWN";
}
private static String getManufacturer() {
try {
String manufacturer = Build.MANUFACTURER;
if (!TextUtils.isEmpty(manufacturer)) {
return manufacturer.toLowerCase();
}
} catch (Throwable ignore) {/**/}
return "UNKNOWN";
}
private static boolean isRightRom(final String brand, final String manufacturer, final String... names) {
for (String name : names) {
if (brand.contains(name) || manufacturer.contains(name)) {
return true;
}
}
return false;
}
中心思路是直接遍历 decorView 找到导航栏的控件,去判别它是否躲藏还是显现。。。
其实不说全面屏手机了,便是我的老华为 7.0系统的手机都判别的不准确,巨坑!
比方,全面屏手机的导航栏判别:
看到我全面屏手势的小横杠杠的了吗?我明明没有底部导航栏了,居然判别我存在导航栏,还给一个彻底不合理的状态栏高度。
我醉了,真的是够了!
而以上办法都是能够经过 WindowInsets 来处理的,也便是为什么推荐部分场景下的一些作用还是运用 WindowInsets 来做为好。
那么咱们真的在实战中运用了 WindowInsetsControllerCompat 就完美了吗?就没坑了吗?
no no no, 答案是否定的。你根本不知道会发生什么兼容性的问题。(兼容性可用说是咱们安卓人的终身之敌)
WindowInsetsController 的兼容性问题
咱们知道 WindowInsetsController 是安卓11以上用的,而 WindowInsetsControllerCompat 是安卓5以上可用的兼容包,那么 WindowInsetsControllerCompat 的兼容包就没有兼容性问题了吗?一样有!
例如一些 WindowInsetsControllerCompat 的获取办法,设置状态栏文本图标的色彩办法,设置导航栏的图标色彩办法。设置状态栏导航栏的背景色彩等。
假如 WindowInsetsController / WindowInsets的办法在某些作用上并没有那么好用,那么咱们是不是还是要用flag的办法来完成这些作用,在一些兼容性好的办法上,那么咱们就能够用 WindowInsetsController / WindowInsets的办法的办法来完成,这样是不是就能相对完美的完成咱们想要的作用了。
所以我封装了这样的东西类。
四、推荐的东西类
此东西类5.0以上可用,记录了一些状态栏与导航栏操作的常用的办法。
public class StatusBarHostUtils {
// ======================= StatusBar begin ↓ =========================
/**
* 5.0以上设置沉溺式状态
*/
public static void immersiveStatusBar(Activity activity) {
//办法一
//false 表明沉溺,true表明不沉溺
// WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false);
//办法二:增加Flag,两种办法都能够,都是5.0以上运用
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.setStatusBarColor(Color.TRANSPARENT);
}
}
/**
* 设置当时页面的状态栏色彩,运用宿主计划一般不用这个修正色彩,仅仅用于沉溺式之后修正状态栏色彩为通明
*/
public static void setStatusBarColor(Activity activity, int statusBarColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(statusBarColor);
}
}
/**
* 6.0版别及以上能够设置黑色的状态栏文本
*
* @param activity
* @param dark 是否需求黑色文本
*/
public static void setStatusBarDarkFont(Activity activity, boolean dark) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
if (dark) {
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
/**
* 老的办法获取状态栏高度
*/
private static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
/**
* 新办法获取状态栏高度
*/
public static void getStatusBarHeight(Activity activity, HeightValueCallback callback) {
getStatusBarHeight(activity.findViewById(android.R.id.content), callback);
}
/**
* 新办法获取状态栏高度
*/
public static void getStatusBarHeight(View view, HeightValueCallback callback) {
boolean attachedToWindow = view.isAttachedToWindow();
if (attachedToWindow) {
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(view);
assert windowInsets != null;
int top = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).top;
int bottom = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).bottom;
int height = Math.abs(bottom - top);
if (height > 0) {
callback.onHeight(height);
} else {
callback.onHeight(getStatusBarHeight(view.getContext()));
}
} else {
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(v);
assert windowInsets != null;
int top = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).top;
int bottom = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).bottom;
int height = Math.abs(bottom - top);
if (height > 0) {
callback.onHeight(height);
} else {
callback.onHeight(getStatusBarHeight(view.getContext()));
}
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
}
}
// ======================= NavigationBar begin ↓ =========================
/**
* 5.0以上-设置NavigationBar底部导航栏的沉溺式
*/
public static void immersiveNavigationBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
View decorView = window.getDecorView();
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.setNavigationBarColor(Color.TRANSPARENT);
}
}
/**
* 设置底部导航栏的色彩
*/
public static void setNavigationBarColor(Activity activity, int navigationBarColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setNavigationBarColor(navigationBarColor);
}
}
/**
* 底部导航栏的Icon色彩白色和灰色切换,高版别系统才会生效
*/
public static void setNavigationBarDrak(Activity activity, boolean isDarkFont) {
WindowInsetsControllerCompat controller = ViewCompat.getWindowInsetsController(activity.findViewById(android.R.id.content));
if (controller != null) {
if (!isDarkFont) {
controller.setAppearanceLightNavigationBars(false);
} else {
controller.setAppearanceLightNavigationBars(true);
}
}
}
/**
* 老的办法获取导航栏的高度
*/
private static int getNavigationBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
/**
* 获取底部导航栏的高度
*/
public static void getNavigationBarHeight(Activity activity, HeightValueCallback callback) {
getNavigationBarHeight(activity.findViewById(android.R.id.content), callback);
}
/**
* 获取底部导航栏的高度
*/
public static void getNavigationBarHeight(View view, HeightValueCallback callback) {
boolean attachedToWindow = view.isAttachedToWindow();
if (attachedToWindow) {
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(view);
assert windowInsets != null;
int top = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).top;
int bottom = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
int height = Math.abs(bottom - top);
if (height > 0) {
callback.onHeight(height);
} else {
callback.onHeight(getNavigationBarHeight(view.getContext()));
}
} else {
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(v);
assert windowInsets != null;
int top = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).top;
int bottom = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
int height = Math.abs(bottom - top);
if (height > 0) {
callback.onHeight(height);
} else {
callback.onHeight(getNavigationBarHeight(view.getContext()));
}
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
}
}
// ======================= NavigationBar StatusBar Hide Show begin ↓ =========================
/**
* 显现躲藏底部导航栏(留意不是沉溺式作用)
*/
public static void showHideNavigationBar(Activity activity, boolean isShow) {
View decorView = activity.findViewById(android.R.id.content);
WindowInsetsControllerCompat controller = ViewCompat.getWindowInsetsController(decorView);
if (controller != null) {
if (isShow) {
controller.show(WindowInsetsCompat.Type.navigationBars());
controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH);
} else {
controller.hide(WindowInsetsCompat.Type.navigationBars());
controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
}
}
}
/**
* 显现躲藏顶部的状态栏(留意不是沉溺式作用)
*/
public static void showHideStatusBar(Activity activity, boolean isShow) {
View decorView = activity.findViewById(android.R.id.content);
WindowInsetsControllerCompat controller = ViewCompat.getWindowInsetsController(decorView);
if (controller != null) {
if (isShow) {
controller.show(WindowInsetsCompat.Type.statusBars());
} else {
controller.hide(WindowInsetsCompat.Type.statusBars());
}
}
}
/**
* 当时是否显现了底部导航栏
*/
public static void hasNavigationBars(Activity activity, BooleanValueCallback callback) {
View decorView = activity.findViewById(android.R.id.content);
boolean attachedToWindow = decorView.isAttachedToWindow();
if (attachedToWindow) {
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(decorView);
if (windowInsets != null) {
boolean hasNavigationBar = windowInsets.isVisible(WindowInsetsCompat.Type.navigationBars()) &&
windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0;
callback.onBoolean(hasNavigationBar);
}
} else {
decorView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(v);
if (windowInsets != null) {
boolean hasNavigationBar = windowInsets.isVisible(WindowInsetsCompat.Type.navigationBars()) &&
windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom > 0;
callback.onBoolean(hasNavigationBar);
}
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
}
}
}
关于状态栏的沉溺式两种办法都能够,而导航栏的沉溺式运用的Flag,修正状态栏与导航栏的背景色彩运用flag,修正状态栏文本色彩运用flag,修正导航栏的图片色彩运用的 controller,获取导航栏状态栏的高度运用的 controller ,判别导航栏是否存在运用的 controller。
一些作用如图:
总结
因为运用了 WindowInsetsController与其兼容库,所以咱们界说的东西类在5.0版别以上。
假如运用flag的办法,那么咱们能够兼容到更低的版别,这一点还请知悉。
在5.0版别以上运用东西类,咱们有些兼容性欠好的运用的是flag计划,而有些作用比较好的咱们运用的是 indowInsetsController 计划。
此计划并非什么威望计划,仅仅我个人在开发过程中踩坑踩出来的,对我个人来说相对完善的一个计划,在实战开发中我个人觉得还算能用。
当然因为各种原因受限,个人水平也有限,不免有凭空捏造的情况,假如你有更好的计划或者觉得有错漏的地方,还望指出来咱们一同交流学习进步。
后期我也会针对本文进行一些扩展,会出一些相关的细节文章与一些作用的完成。
好了,本文的悉数代码与Demo都现已开源。有爱好能够看这儿。项目会继续更新,咱们能够关注一下。
假如感觉本文对你有一点点的启发,还望你能点赞
支撑一下,你的支撑是我最大的动力。
Ok,这一期就此结束。