关于Android状态栏高度为0仍显示的问题

2,242 阅读4分钟

前言

这里有一个比较坑的事,系统将状态栏的高度已经设置为0,然后界面上确实已经看不到时间,WiFi 等图标,也无法通过下拉,显示通知栏。但在某些应用的activity上,还是会出现activity的状态栏,这个一开始还以为是activity的标题栏,但到了后面,发现这是状态栏。

应用层更改Activity的窗口风格

而这里的状态栏可以在activity的onCreate方法中通过如下方法隐藏掉:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Window window = getWindow();
    window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);//隐藏状态栏

    setContentView(R.layout.activity_main);
}

这里需要注意的是,所有的窗口属性设置,例如隐藏导航栏,隐藏标题栏,更改状态栏的颜色,都必须在调用如下方法之前:

setContentView(R.layout.activity_main);

这是因为,在该执行该方法的时候,就开始去获取窗口的相关属性,然后去进行窗口的绘制,如果,在这之后再去设置属性,会出现报错或者设置无效的情况。

通过系统更改Activity的窗口风格

但此次涉及修改的APP比较多,所以想通过系统来进行实现,将状态栏彻底去掉。

既然在应用层中可以通过方法来解决问题,那么我们只需在源码中搜索setContentView的具体实现,然后再找到合适的位置,在绘制窗口前进行设置即可。

setContentView属于activity的方法,我们查看该方法的实现:

frameworks/base/core/java/android/app/Activity.java
public void setContentView(View view) {
    getWindow().setContentView(view);
    initWindowDecorActionBar();
}

这里通过getWindow方法去获取获取一个实例,然后再调用该实例中的方法。我们先查看getWindow的实现:

frameworks/base/core/java/android/app/Activity.java
public Window getWindow() {
    return mWindow;
}

这里返回一个全局实例,继续查看该实例的实现,最后发现该实例实在activity的attach方法中实现:

frameworks/base/core/java/android/app/Activity.java
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback) {
        
    ....
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    
    ....
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;

    mWindow.setColorMode(info.colorMode);
    
    ...
}

原来最终是通过PhoneWindow类的方法来实现,继续查看:

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
public void setContentView(View view, ViewGroup.LayoutParams params) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        view.setLayoutParams(params);
        final Scene newScene = new Scene(mContentParent, view);
        transitionTo(newScene);
    } else {
        mContentParent.addView(view, params);
    }
    mContentParent.requestApplyInsets();
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
    mContentParentExplicitlySet = true;
}

首次启动activity的时候,mContentParent为null,那么就会先执行installDecor方法,进来看看该方法的实现:

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
private void installDecor() {
    ....
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
        ....
    }
    ....
}

mContentParent通过generateLayout获得实例,我们看下generateLayout方法的实现:

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
protected ViewGroup generateLayout(DecorView decor) {
    TypedArray a = getWindowStyle();
    final Context context = getContext();
    final Context context = getContext();
    ....
    mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
    int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
            & (~getForcedWindowFlags());
    if (mIsFloating) {
        setLayout(WRAP_CONTENT, WRAP_CONTENT);
        setFlags(0, flagsToUpdate);
    } else {
        setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
    }

    if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
        requestFeature(FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
        // Don't allow an action bar if there is no title.
        requestFeature(FEATURE_ACTION_BAR);
    }

    if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
        requestFeature(FEATURE_ACTION_BAR_OVERLAY);
    }

    if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {
        requestFeature(FEATURE_ACTION_MODE_OVERLAY);
    }

    if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
        requestFeature(FEATURE_SWIPE_TO_DISMISS);
    }
    //add by mnq. 隐藏状态栏 @{
    if (context.getResources().getBoolean(R.bool.cvte_hide_activity_statusbar) || a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
    }
    //add by mnq. 隐藏状态栏 @}

    if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
            false)) {
        setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
                & (~getForcedWindowFlags()));
    }
    ....
}

在与getWindowStyle四目相对的那一刻,我就知道,我们要找的地方到了。继续往下查看,果然都是一些设置Window风格的语句,在这里找到了我们这次的目标:

if ( a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
}

这里是通过去读取相关的资源配置,如果配置了Window全屏显示,则设置FLAG_FULLSCREEN的flag,然后Window在绘制的时候,就会进行全屏显示,而不会有状态栏。

而这个资源配置,则是各个APP的Androidmanifest中进行配置的Window风格。显然我们改动不了这里。但我们可以在这里增加一个配置,然后在overlay中进行配置实现:

1.
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
//add by mnq. 隐藏状态栏 @{
if (context.getResources().getBoolean(R.bool.cvte_hide_activity_statusbar) || a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
        setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
}
//add by mnq. 隐藏状态栏 @}

2.
frameworks/base/core/res/res/values/config.xml
<!-- add by mnq. 隐藏状态栏 @{ -->
<bool name="cvte_hide_activity_statusbar">false</bool>
<!-- add by mnq. 隐藏状态栏 @} -->

3.
frameworks/base/core/res/res/values/symbols.xml
<!-- add by mnq. 隐藏状态栏 @{ -->
<java-symbol type="bool" name="cvte_hide_activity_statusbar" />
<!-- add by mnq. 隐藏状态栏 @} -->

以上就相当于在系统中增加了一个配置,该配置可以通过overlay去配置是否要通过系统来强制隐藏activity的状态栏。

总结

所以,所有在Androidmanifest中配置的窗口风格,皆可以在这里通过系统去强制改写。

当然,能够在Androidmanifest中进行配置就最好了。

互动

如果文章存在错误描述,可直接留言,一起探讨!

可能感兴趣的文章

踩坑之默认输入法配置

踩坑之NavigationBar 的隐藏与否

关于Android9.0开机黑屏一段时间才加载launcher界面的解决方法

最后

我在微信公众号也有写文章,更新比较及时,有兴趣者可以关注如下公众号!