关于沉浸式状态栏我们首先都要设置为全屏和透明导航栏。
<!--状态栏设置为透明背景-->
<item name="colorPrimary">@color/translate</item>
<!--设置全屏-->
<item name="android:windowFullscreen">true</item>
我们都知道当设置为全屏模式的时候,我们的视图会跑到状态栏的后面。所以在做沉浸式状态栏的时候需要解决的就是被状态栏遮盖的问题。
那我们思考下这个问题如何解决~
*
*
*
*
*
*
思考结束,结果就是,无外乎就是计算状态栏的高度,然后给被覆盖的view一个marginTop或者给父视图一个paddingTop。我知道说到现在会有很多人觉得这个方式太粗暴了不优雅,那现在给你看看官方是如何实现的,如何让你的代码变得优雅起来。
官方给我们提供了一个属性:android:fitsSystemWindows="true",当在布局的最顶层视图设置了这个属性,我们的遮盖问题就解决了。现在我先不说他的缺点,我先给你分析下他的具体实现:
首先看这个View有没有设置属性fitsSystemWindows,设置有的话给viewFlagValues设置FITS_SYSTEM_WINDOWS标志位。
case R.styleable.View_fitsSystemWindows:
if (a.getBoolean(attr, false)) {
viewFlagValues |= FITS_SYSTEM_WINDOWS;
viewFlagMasks |= FITS_SYSTEM_WINDOWS;
}
break;
View的onApplyWindowInsets方法会被调用,WindowInsets这个类里面保存了整个视图窗口被系统视图占据的位置数据,我们也就是根据这个类里面提供的数据来知道状态栏的高度的。
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if ((mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
&& (mViewFlags & FITS_SYSTEM_WINDOWS) != 0) {
return onApplyFrameworkOptionalFitSystemWindows(insets);
}
if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) == 0) {
if (fitSystemWindows(insets.getSystemWindowInsetsAsRect())) {
return insets.consumeSystemWindowInsets();
}
} else {
// We were called from within a direct call to fitSystemWindows.
if (fitSystemWindowsInt(insets.getSystemWindowInsetsAsRect())) {
return insets.consumeSystemWindowInsets();
}
}
return insets;
}
private boolean fitSystemWindowsInt(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
Rect localInsets = sThreadLocal.get();
boolean res = computeFitSystemWindows(insets, localInsets);
applyInsets(localInsets);
return res;
}
return false;
}
private void applyInsets(Rect insets) {
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
mUserPaddingLeftInitial = insets.left;
mUserPaddingRightInitial = insets.right;
internalSetPadding(insets.left, insets.top, insets.right, insets.bottom);
}
protected void internalSetPadding(int left, int top, int right, int bottom) {
final int viewFlags = mViewFlags;
boolean changed = false;
if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) {
if (mPaddingLeft != left) {
changed = true;
mPaddingLeft = left;
}
if (mPaddingTop != top) {
changed = true;
mPaddingTop = top;
}
if (mPaddingRight != right) {
changed = true;
mPaddingRight = right;
}
if (mPaddingBottom != bottom) {
changed = true;
mPaddingBottom = bottom;
}
if (changed) {
requestLayout();
invalidateOutline();
}
}
可以看到通过这一系列走下就是给我们的父视图设置了mPaddingTop从而实现了视图不被遮挡的问题。
现在介绍一个更加灵活的方式WindowInsetsController,这个类是谷哥官方提供给咱们的,使用方式如下:
ViewCompat.setOnApplyWindowInsetsListener(button) { view, insets ->
val params = view.layoutParams as FrameLayout.LayoutParams
params.topMargin = insets.systemWindowInsetTop
insets
}
这就很优雅灵活了。 参考链接。
通过这次的学习,我发现了几个官方提供的比较有意思的新类: