Force stacked tabs

2019-01-24 16:37发布

问题:

Is there any way to force stacked tabs? I want tabs separate of action bar (in a second row), even when landscape mode.

I am trying to force it but I can´t. For example, Twitter app in Android, when change to lanscape mode, continue showing two rows (tabs in a separate rows, which are known as stacked tabs).

Thanks!

回答1:

Not only can you not force stacked tabs, you can't even force tabs -- Android can and will replace them with a drop-down list for navigation in some screen sizes and orientations.

Your only solution is to move away from action bar tabs, such as by using ViewPager and PagerTabStrip.



回答2:

I had luck using the following reflection 'hack':

private void forceStackedTabs() {
    ActionBar ab = getSupportActionBar();
    if ( ab instanceof ActionBarImpl ) {
        // Pre-ICS
        disableEmbeddedTabs( ab );
    } else if ( ab instanceof ActionBarWrapper ) {
        // ICS
        try {
            Field abField = ab.getClass().getDeclaredField( "mActionBar" );
            abField.setAccessible( true );
            disableEmbeddedTabs( abField.get( ab ) );
        } catch (NoSuchFieldException e) {
            Log.e( TAG, "Error disabling actionbar embedded", e );
        } catch (IllegalArgumentException e) {
            Log.e( TAG, "Error disabling actionbar embedded", e );
        } catch (IllegalAccessException e) {
            Log.e( TAG, "Error disabling actionbar embedded", e );
        }
    }
}
private void disableEmbeddedTabs(Object ab) {
    try {
        Method setHasEmbeddedTabsMethod = ab.getClass().getDeclaredMethod("setHasEmbeddedTabs", boolean.class);
        setHasEmbeddedTabsMethod.setAccessible(true);
        setHasEmbeddedTabsMethod.invoke(ab, false);
    } catch (Exception e) {
        Log.e( TAG, "Error disabling actionbar embedded", e );
    }
}

Please note that I didn't think of this myself, but simply rewrote the code given in this answer: replicate ActionBar Tab(s) with custom view



回答3:

I used this to force ActionBar stacked or unstacked tabs from Gingerbread to KitKat.

Modded From: http://www.blogc.at/2014/01/23/android-tabs-appear-above-or-below-actionbar/

setHasEmbeddedTabs(mActionbar,false);

    public static void setHasEmbeddedTabs(Object inActionBar, final boolean inHasEmbeddedTabs)
    {
        // get the ActionBar class
        Class<?> actionBarClass = inActionBar.getClass();

        // if it is a Jelly Bean implementation (ActionBarImplJB), get the super class (ActionBarImplICS)
        if ("android.support.v7.app.ActionBarImplJB".equals(actionBarClass.getName()))
        {
            actionBarClass = actionBarClass.getSuperclass();
        }

        // if Android 4.3 >
        if ("android.support.v7.app.ActionBarImplJBMR2".equals(actionBarClass.getName())){
            actionBarClass = actionBarClass.getSuperclass().getSuperclass();
        }

        try
        {
            // try to get the mActionBar field, because the current ActionBar is probably just a wrapper Class
            // if this fails, no worries, this will be an instance of the native ActionBar class or from the ActionBarImplBase class
            final Field actionBarField = actionBarClass.getDeclaredField("mActionBar");
            actionBarField.setAccessible(true);
            inActionBar = actionBarField.get(inActionBar);
            actionBarClass = inActionBar.getClass();
        }
        catch (IllegalAccessException e) {}
        catch (IllegalArgumentException e) {}
        catch (NoSuchFieldException e) {}

        try
        {
            // now call the method setHasEmbeddedTabs, this will put the tabs inside the ActionBar
            // if this fails, you're on you own <img class="wp-smiley" alt=";-)" src="http://www.blogc.at/wp-includes/images/smilies/icon_wink.gif">
            final Method method = actionBarClass.getDeclaredMethod("setHasEmbeddedTabs", new Class[] { Boolean.TYPE });
            method.setAccessible(true);
            method.invoke(inActionBar, new Object[]{ inHasEmbeddedTabs });
        }
        catch (NoSuchMethodException e)        {}
        catch (InvocationTargetException e) {}
        catch (IllegalAccessException e) {}
        catch (IllegalArgumentException e) {}
    }


回答4:

If you need to support phones and tablets and don't want to use separate implementation you can put this in your activity:

@Override
public Resources getResources() {
    if (mResourcesImpl == null) {
        mResourcesImpl = new ResourcesImpl(super.getResources());
    }
    return mResourcesImpl;
}

class ResourcesImpl extends Resources {
    private Resources mResources;
    private Set<Integer> mActionBarEmbedTabsIds = new HashSet<Integer>();

    ResourcesImpl(Resources resources) {
        super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());

        mResources = resources;

        String packageName = getPackageName();
        mActionBarEmbedTabsIds.add(mResources.getIdentifier("abc_action_bar_embed_tabs", "bool", packageName));
        mActionBarEmbedTabsIds.add(mResources.getIdentifier("abc_action_bar_embed_tabs_pre_jb", "bool", packageName));
        mActionBarEmbedTabsIds.add(mResources.getIdentifier("action_bar_embed_tabs", "bool", "android"));
        mActionBarEmbedTabsIds.add(mResources.getIdentifier("action_bar_embed_tabs_pre_jb", "bool", "android"));
        mActionBarEmbedTabsIds.remove(0);
    }

    @Override
    public boolean getBoolean(int id) throws NotFoundException {
        if (mActionBarEmbedTabsIds.contains(id)) {
            return areActionBarTabsEmbed(); // stacked ot embed goes here
        }
        return super.getBoolean(id);
    }
}