16

So I was using the ActionBarSherlock and decided to switch to the new ActionBarCompat. With ABS, hiding the ActionBar was possible using the way described in this post: How to hide action bar before activity is created, and then show it again?

But, with the ActionBarCompat the app crashes on API14, because when you set android:windowActionBar as false the getSupportActionBar() method returns null, even if you have declared the getWindow().requestFeature(Window.FEATURE_ACTION_BAR); into the onCreate() method.

Funny thing is that if you call getActionBar() instead, you get the object and everything works fine.

So, is that a bug or am I missing something? Any ideas are welcome!


styles.xml file:

<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 file:

...
@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();
}
Community
  • 1
  • 1
Mokkun
  • 1,796
  • 1
  • 23
  • 32
  • @Johnson [accordingly to the docs](http://android-developers.blogspot.jp/2013/08/actionbarcompat-and-io-2013-app-source.html), nope. _"ActionBarCompat contains it’s own ActionBar class, and to retrieve the Action Bar attached to your activity you call getSupportActionBar()."_ – Mokkun Aug 30 '13 at 06:30
  • did you ever solve this? I'm running into the same issue. – Studio4Development Nov 21 '13 at 14:42
  • @Studio4Development nope, the best option would be to create a class to handle the different types of ActionBar (native and support library) and make the call to the right ones. That's one idea, haven't used it yet tho, still using ABS instead of ABC. :/ – Mokkun Nov 22 '13 at 03:14

4 Answers4

19

I got stuck with the same problem and, it seems to me, found a reason of this strange behavior. I looked through source of support library and got this:

Appcompat checks a value of mHasActionBar variable before creating new action bar in 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();
    ...

And we can change its value by calling supportRequestWindowFeature(int featureId) which is delegated by ActionBarActivity to a ActionBarActivityDelegate.

There are base delegate class ActionBarDelegateBase and its descendants ActionBarDelegateHC, ActionBarActivityDelegateICS, ActionBarActivityJB, one of which is chosen according to a version of running android. And method supportRequestWindowFeature is actually works fine almost in all of them, but it's overridden in ActionBarActivityDelegateICS like that

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

So it has no effect on the variable mHasActionBar, that's why getSupportActionBar() returns null.

We almost there. I came to two different solutions.

First way

  1. import source project of appcompat from git

  2. change overridden method in ActionBarActivityDelegateICS.java to something like this

    @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. place this line in activity's onCreate method before getSupportActionBar()

    supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR);
    

Second way

  1. import project of appcompat from android SDK (which is with empty src directory)

  2. add this method to your activity

    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);
        }
    }
    
  3. call requestFeature() in onCreate method of your activity like this

    if (Build.VERSION.SDK_INT >= 11) {
        requestFeature();
    }
    supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR);
    

I used the second way. That's all.

st_
  • 458
  • 5
  • 11
  • 1
    This worked for me. I realized I needed getSupportActionBar() otherwise Navigation DrawerToggle doesn't work properly. On KitKat I had to call supportRequestWindowFeature(Window.FEATURE_ACTION_BAR) before the requestFeature() method otherwise I got an exception. It feels like a bit of hack I hope it gets resolved in a future release of the support library. – Ish Sep 16 '14 at 02:39
  • 1
    When using ProGuard, I had to add those two lines in my app's proguard .cfg file: `-keepclassmembers class android.support.v7.app.ActionBarActivity { android.support.v7.app.ActionBarActivityDelegate mImpl; }` and `-keepclassmembers class android.support.v7.app.ActionBarActivityDelegate { boolean mHasActionBar; }` Maaan, comments formatting sucks. – nonzaprej Aug 13 '15 at 00:51
5

I use this to hide ActionBar in AppCompat: 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>
Mike
  • 2,547
  • 3
  • 16
  • 30
0

I don`t know if I understood exactly your question, but here I go.

I think that you need to use both: getSupportActionBar() to old versions and getActionBar() to newest versions. It`s not a bug.

You need to verify the device version before use the methods.

I hope I was able to help.

gvee
  • 16,732
  • 35
  • 50
  • I've tried that too, problem is that the types from getSupportActionBar() and getActionBar() are different, that way I'll have to duplicate all the code to work with both types. – Mokkun Nov 19 '13 at 07:18
0

Does your activity extends ActionBarActivity? Probably it doesn't and that's why it's running with the getActionbar() method.

Levente Kürti
  • 1,005
  • 1
  • 12
  • 23
  • sorry for the late reply, yes, I'm extending the ActionBarActivity. Now that I have time I'll try some things and put more details in here soon. :) – Mokkun Nov 19 '13 at 07:16