ActionBarCompat:在活动创建之前隐藏ActionBar(错误?)

16

我曾经使用ActionBarSherlock,但决定转换到新的ActionBarCompat。在ABS中,可以使用本文描述的方式隐藏ActionBar:

How to hide action bar before activity is created, and then show it again?

但是,在ActionBarCompat上,当你将android:windowActionBar设置为false时,即使你已经在onCreate()方法里声明了getWindow().requestFeature(Window.FEATURE_ACTION_BAR);方法,getSupportActionBar()方法仍然会返回null,而应用程序会在API14上崩溃。

有趣的是,如果您改为调用getActionBar(),则可以获得对象,一切都能正常工作。

所以,这是一个bug还是我漏掉了什么?欢迎任何想法!


styles.xml文件:

<style name="Theme.MyApp" parent="@style/Theme.AppCompat.Light.DarkActionBar">
    <item name="android:windowActionBar">false</item>
    <item name="android:windowTitleSize">0dp</item>
</style>

MyActivity.java文件:

...
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Get the action bar feature. This feature is disabled by default into the theme
    // for specific reasons.
    getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
    ...
    // By default the action bar is hidden.
    getSupportActionBar().hide();
}

根据文档,@Johnson,不是这样的。"ActionBarCompat包含自己的ActionBar类,要检索附加到您的活动的操作栏,您需要调用getSupportActionBar()方法。" - Mokkun
你解决过这个问题吗?我遇到了同样的问题。 - Studio4Development
@Studio4Development 不,最好的选择是创建一个类来处理不同类型的ActionBar(原生和支持库),并调用正确的ActionBar。这是一个想法,但我还没有使用过它,仍然在使用ABS而不是ABC。 :/ - Mokkun
4个回答

19

我遇到了同样的问题,但是似乎找到了这种奇怪行为的原因。我查阅了支持库的源代码,并得到了以下信息:

在创建新的操作栏之前,Appcompat 检查 mHasActionBar 变量的值,具体请参见 ActionBarActivityDelegate

final ActionBar getSupportActionBar() {
    // The Action Bar should be lazily created as mHasActionBar or mOverlayActionBar
    // could change after onCreate
    if (mHasActionBar || mOverlayActionBar) {
        if (mActionBar == null) {
            mActionBar = createSupportActionBar();
    ...

我们可以通过调用supportRequestWindowFeature(int featureId)来改变其值,该方法是由ActionBarActivity委托给一个ActionBarActivityDelegate

有基本委托类ActionBarDelegateBase及其子类ActionBarDelegateHC, ActionBarActivityDelegateICS,ActionBarActivityJB,其中一个根据正在运行的Android版本来选择。方法supportRequestWindowFeature在几乎所有情况下都可以正常工作,但是在ActionBarActivityDelegateICS中被覆盖,具体如下:

@Override
public boolean supportRequestWindowFeature(int featureId) {
    return mActivity.requestWindowFeature(featureId);
}

因此,它对变量mHasActionBar没有影响,这就是为什么getSupportActionBar()返回null。

我们快要完成了。我想到了两种不同的解决方案。

第一种方法

  1. git导入appcompat的源项目。

  2. 将ActionBarActivityDelegateICS.java中的重写方法更改为类似以下内容:

    @Override
    public boolean supportRequestWindowFeature(int featureId) {
        boolean result = mActivity.requestWindowFeature(featureId);
        if (result) {
            switch (featureId) {
            case WindowCompat.FEATURE_ACTION_BAR:
                mHasActionBar = true;
            case WindowCompat.FEATURE_ACTION_BAR_OVERLAY:
                mOverlayActionBar = true;
            }
        }
        return result;
    }
    
  3. 将此行代码放在活动的onCreate方法中,在调用getSupportActionBar()之前。

  4. supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR);
    

第二种方法

  1. 从Android SDK中导入appcompat项目(其中包含空的src目录)

  2. 将此方法添加到您的活动中

private void requestFeature() {
    try {
        Field fieldImpl = ActionBarActivity.class.getDeclaredField("mImpl");
        fieldImpl.setAccessible(true);
        Object impl = fieldImpl.get(this);

        Class<?> cls = Class.forName("android.support.v7.app.ActionBarActivityDelegate");

        Field fieldHasActionBar = cls.getDeclaredField("mHasActionBar");
        fieldHasActionBar.setAccessible(true);
        fieldHasActionBar.setBoolean(impl, true);

    } catch (NoSuchFieldException e) {
        Log.e(LOG_TAG, e.getLocalizedMessage(), e);
    } catch (IllegalAccessException e) {
        Log.e(LOG_TAG, e.getLocalizedMessage(), e);
    } catch (IllegalArgumentException e) {
        Log.e(LOG_TAG, e.getLocalizedMessage(), e);
    } catch (ClassNotFoundException e) {
        Log.e(LOG_TAG, e.getLocalizedMessage(), e);
    }
}
  • 在您的活动的onCreate方法中调用requestFeature(),如下所示

  • if (Build.VERSION.SDK_INT >= 11) {
        requestFeature();
    }
    supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR);
    
    我使用了第二种方法。就这样。

    1
    这对我有用。我意识到我需要调用 getSupportActionBar() 方法,否则 Navigation DrawerToggle 就无法正常工作。在 KitKat 上,我必须在调用 requestFeature() 方法之前调用 supportRequestWindowFeature(Window.FEATURE_ACTION_BAR) 方法,否则就会抛出异常。感觉有点像 hack,希望在未来的支持库版本中得到解决。 - Ish
    1
    当使用ProGuard时,我必须在我的应用程序的proguard.cfg文件中添加这两行:`-keepclassmembers class android.support.v7.app.ActionBarActivity { android.support.v7.app.ActionBarActivityDelegate mImpl; }`-keepclassmembers class android.support.v7.app.ActionBarActivityDelegate { boolean mHasActionBar; } 天啊,注释格式糟糕透了。 - nonzaprej

    5
    我使用以下代码在AppCompat中隐藏ActionBar: style.xml
    <style name="Theme.AppCompat.Light.NoActionBar" parent="@style/Theme.AppCompat.Light">
        <item name="android:windowNoTitle">true</item>
    </style>
    

    AndroidManifest.xml:

    <activity
            android:name=".Splash"
            android:label="@string/title_activity_splash"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
    </activity>
    

    这会在Gingerbread上隐藏ActionBar吗?我一直在努力。它可以在较高的API上工作。 - bajicdusko
    这适用于API级别14+。谢谢。 - zackygaurav

    0

    我不确定我是否完全理解了你的问题,但是我会尝试回答。

    我认为你需要同时使用 getSupportActionBar()getActionBar()。对于旧版本,你需要使用前者,而对于新版本,则需要使用后者。这并不是一个错误。

    在使用这些方法之前,你需要验证设备的版本。

    希望我的回答能够帮到你。


    我也尝试过,问题是getSupportActionBar()和getActionBar()返回的类型不同,这样我就必须复制所有代码来处理这两种类型。 - Mokkun

    0

    您的活动是否扩展ActionBarActivity? 可能不是,这就是为什么它使用getActionbar()方法运行的原因。


    抱歉回复晚了,是的,我正在扩展ActionBarActivity。现在有时间了,我会尝试一些东西并很快在这里添加更多细节。 :) - Mokkun

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接