android 4.0, text on the action bar NEVER shows
Asked Answered
H

8

60

I am trying to use the new api's from google, specifically the action bar.

When the build was set at api 10, if I pressed the menu button, I got nice looking menu options, each with a picture and icon. When using api 14, No matter what I try, it always puts the icon in the action bar with NO text. I have tried everything I can think of. I gave it the "with text" property, changed the text to a single character (in case it was a room issue), but nothing.

I have seen this done before, even in the developer guide at android.developer, but I can't seem to find an answer as to HOW to get it to show up.

Halvorson answered 14/2, 2012 at 18:21 Comment(1)
could you show your menu.xml for the activity? I may have a solutionLatreese
A
84

I suspect that it was a conscious decision by the Android developers to never display a single menu item's text and icon on a narrow action bar. But if you really want to do so, you can use android:actionLayout in your menu.xml file. The Android ActionBar documentation has a slightly better explanation.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_foo"
          android:title="@string/menu_foo"
          android:icon="@drawable/ic_menu_foo"
          android:showAsAction="always"
          android:actionLayout="@layout/action_button_foo" />
</menu>

Then create your action_button_foo.xml layout:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingTop="14dp"
    android:paddingBottom="14dp"
    android:gravity="center"
    android:text="@string/menu_foo"
    android:drawableLeft="@drawable/ic_menu_foo"
    android:background="@drawable/bg_btn_action_bar"
    android:clickable="true" />

and use a selector for its background bg_btn_action_bar.xml, so it changes color when you tap it:

<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@drawable/bg_action_bar_pressed" />
    <item
        android:drawable="@color/transparent" />
</selector>

Now you'll need to make your custom view handle click events. In your Activity, I like to do this, so that I can handle the click in onOptionsItemSelected along with all my other, non-custom items.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.my_menu, menu);

    final MenuItem item = menu.findItem(R.id.menu_foo);
    item.getActionView().setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            onOptionsItemSelected(item);
        }
    });

    return super.onCreateOptionsMenu(menu);
}
Avicenna answered 1/9, 2012 at 7:34 Comment(2)
If you're using ActionBarSherlock, @color/ and @dimen/ will have some options that start with "abs__" . These are the values ActionBarSherlock is using; you can use them to make your custom layout look right. For example, you want @color/abs__holo_blue_light as the highlight color.Cohen
This approach is pretty straightforward but it's tricky to get the styling just right. I found the style attributes that are used in ABS so it is identical to the native styling. I put it into this gist in case anybody else finds it useful: gist.github.com/marczych/6908324Transformer
C
10

This is definitely the same thing I've observed on my Nexus S running 4.0.4. My app uses an action bar with several tabs that are implemented as fragments. My various fragments make adjustments to the menu options displayed on the action bar while the their tab is visible.

This appears to be a bug in ICS, because it performs consistently as follows, both on my Nexus S and in the emulator (both HVGA and WVGA800):

  1. In portrait mode, my logo/up button appears on the top row of the action bar, tabs appear on the second row, and any actions appear as icons only (no text) in the right side of the top row.
  2. But if when I rotate to landscape, the action bar collapses to a single row, and tabs move up to the top bar as a spinner (drop-down list) next to my up button. But notably, then the text appears next to my action icons.

I noticed some other glitches with the tab spinner that lead me to believe that this little corner of ICS is a bit messy/buggy. If I tell the application to split the action bar on narrow displays (by adding android:uiOptions="splitActionBarWhenNarrow" in the manifest, ICS always pushes those items to the bottom bar, even though there's still plenty of room at the top. And even with the extra bar, it still doesn't display the text, just the icon.

On my Xoom running 4.0.4, tabs and action items always appear the way you'd expect them to appear because there's plenty of room.

Workaround: if you really want text on the action bar in portrait mode, you need to give up the icon. Remove the icon from your menu item and the text will appear. This isn't exactly what we're after though.

I've posted a bug report here: https://code.google.com/p/android/issues/detail?id=30180.

Countershading answered 5/5, 2012 at 19:19 Comment(0)
T
3

If you want your Options Menu to show up in your action bar with Honeycomb, I did this:

In your activity, override this function:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.actionbar_universe, menu);
    return true;
}

where R.menu.actionbar_universe define your menu item like this:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/crossholdings" android:showAsAction="always|withText"
      android:title="Cross Holdings" android:icon="@drawable/actionbar_cross"/>
</menu>

Note the showAsAction="always|withText" and specify android:title.

If you have that and its not working please copy|paste your menu resource here.

EDIT: This answers the wrong question, but it is the original text.

I use this bit of code to set the title of the action bar, and paint it red with my companies logo. It works well in 3.0.

public ActionBar setActionBarStyle(String title) {
    ActionBar actionBar = setActionBarStyle();
    actionBar.setTitle(title);
    return actionBar;
}

public ActionBar setActionBarStyle() {
    ActionBar actionBar = getActionBar();
    ShapeDrawable actionBackground = new ShapeDrawable();
    actionBackground.getPaint().setColor(Color.RED);
    actionBackground.setBounds(0, 0, 5, 5);
    actionBar.setBackgroundDrawable(actionBackground);
    actionBar.setDisplayUseLogoEnabled(true);
    actionBar.setDisplayHomeAsUpEnabled(true);

    return actionBar;
}
Turbosupercharger answered 14/2, 2012 at 18:52 Comment(8)
Thanks, but doesn't that set the main title of the action bar? I am looking to have the text for each menu item show up. i.e....I have a menu item "Preferences" with a wrench icon, and a menu item "Add" with a + sign icon. I want both the icon and text to appear for each item. Instead I get a picture of the wrench in the bottom left of the action bar (at the bottom of the screen) and the + sign on the bottom right. No Text. If I run using gingerbread, and press the menu button, I see both options popup with the text and icon...just as it should.Halvorson
You are right, I misunderstood. I'll answer again with how I have both text and icon shown in the action bar.Turbosupercharger
Yes, I have those set and it is still not working. Here is my xml <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="schemas.android.com/apk/res/android" > <item android:id="@+id/menu_add" android:title="Add Client" android:icon="@android:drawable/ic_menu_add" android:showAsAction="always|withText"></item><item android:id="@+id/menu_settings"Halvorson
android:icon="@android:drawable/ic_menu_manage" android:title="@string/menu_settings" android:showAsAction="always|withText"/> <item android:id="@+id/menu_about" android:title="Help/About" android:icon="@android:drawable/ic_menu_help" android:showAsAction="always|withText"></item> </menu>Halvorson
Please note, this is for a smartphone running android 4.03. Not a tablet. I bet it would work with a tablet, but it won't work on a phone for some reason.Halvorson
Maybe you are right. My phone is still 2.3.3. I noticed you have a extra semi-colon in your schema declaration. That unlikely to be the problem.. but you never know. Otherwise I see no reason for why this wouldn't work.Turbosupercharger
Ya, that wasn't the problemm...this is driving me nuts. And it seems I am the only one it is happening to as I can't find any other postings anywhere about this.Halvorson
@user1209699 your certainly not the only one also check - code.google.com/p/android/issues/…Billowy
V
3

The "withText" property works with most tablets, but an easy way to get icons and text on smaller devices is to add the text next to the icon as one image (PNG file). That way, both the text and icon will be seen as one icon and the whole thing will display.

You can use the original icon for tablets by using the withText property.

  • You have to create an extra menu folder in the res directory titled "menu-w600dp".

  • The optionmenu.xml in this folder will only apply to screen widths bigger than 600dp (the ones that will show the icons and text with no problems).

Vanscoy answered 9/7, 2013 at 20:49 Comment(2)
Unfortunately not a solution if you're implementing localisationHalima
@Halima You just have to implement localisation the same way as before, but now with an image instead of the text. I assume this issue is probably fixed by now anyway.Vanscoy
M
1

Fixed this issue by reading "If your app is using the Support Library" section under Specify the Actions in XML.

...for compatibility on versions as low as Android 2.1, the showAsAction attribute is not available from the android: namespace. Instead this attribute is provided by the Support Library and you must define your own XML namespace and use that namespace as the attribute prefix. (A custom XML namespace should be based on your app name, but it can be any name you want and is only accessible within the scope of the file in which you declare it.) For example:

 <menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
 <!-- Search, should appear as action button -->
 <item android:id="@+id/action_search"
       android:icon="@drawable/ic_action_search"
       android:title="@string/action_search"
       yourapp:showAsAction="ifRoom"  />
 ...
 </menu>

If none of these other things work for you, this may.

Monserratemonsieur answered 16/4, 2014 at 0:13 Comment(0)
M
1

I tried many options and I came up with a simple "trick" without any weird line of code, without images. And first solution with custom actionLayout simply did not work for me with API level 10 compatibility.

If you want to display text AND icon on a small action bar it means you know you have the space, right? So you can use 2 menu items:

  • First with the icon ONLY (ignore warning, if you set a title tablets will show it twice)
  • Second with the text ONLY

And choose the text action to 'ifRoom' if needed so that if you do need space, the text will go away. It WILL take some more space on the action bar though but was a good compromise for me. I end up with the following code:

<item
    android:id="@+id/menu_save"
    android:icon="@drawable/save"
    pelmel:showAsAction="always">
</item>
<item
    android:id="@+id/menu_save_text"
    android:title="@string/profileSave"
    pelmel:showAsAction="ifRoom">
</item>

(EDIT Where "pelmel" is your app name END EDIT)

And then your selection handler just has to catch both IDs :

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.menu_save:
    case R.id.menu_save_text:
        // Your code here
        return true;
    }
}
Mazza answered 29/6, 2014 at 12:9 Comment(2)
I tried this approach, but the separator line that the ActionBar places between the items makes them look disjoint. Thanks for the suggestion though.Alodi
I edited my answer to add this precision, thank you!Mazza
S
0

Here's another option, based roughly on dgmltn's. The advantages:

  • More control - e.g. I've swapped the text and image over in my layout.
  • Easier to use - only requires two extra lines in your activities/fragments.
  • Only requires two extra files.
  • Possibly slightly more correct, but it's still a bit of a hack IMO.

I've assumed you're using ActionBarSherlock in this example. First, create the view layout you want. This one is based on ActionBarSherlock's. All I changed was swapping the image/view over, reducing the shared margin/padding to 0 so they are closer, and resolving all the ABS styles.

<com.example.views.ActionMenuTextItemView xmlns:android="http://schemas.android.com/apk/res/android"
    style="@android:style/Widget.Holo.ActionButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:addStatesFromChildren="true"
    android:focusable="true"
    android:gravity="center"
    android:clickable="true"
    android:paddingLeft="4dip"
    android:paddingRight="4dip" >

    <com.actionbarsherlock.internal.widget.CapitalizingButton
        android:id="@+id/abs__textButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="@null"
        android:ellipsize="none"
        android:focusable="false"
        android:minHeight="48dip"
        android:minWidth="48dip"
        android:paddingBottom="4dip"
        android:paddingLeft="4dip"
        android:paddingRight="0dip"
        android:paddingTop="4dip"
        android:singleLine="true"
        android:textAppearance="@android:style/TextAppearance.Holo.Widget.ActionBar.Menu"
        android:textColor="#fff3f3f3" />

    <ImageButton
        android:id="@+id/abs__imageButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="4dip"
        android:layout_marginLeft="0dip"
        android:layout_marginRight="4dip"
        android:layout_marginTop="4dip"
        android:adjustViewBounds="true"
        android:background="@null"
        android:focusable="false"
        android:scaleType="fitCenter"
        android:visibility="gone" />

</com.example.views.ActionMenuTextItemView>

Then create the corresponding View class. You may want to copy CapitalizingButton if you are worried about using internal things. Oh, also I never fixed the minimum width stuff. Don't think it really matters though.

package com.example.views;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageButton;
import android.widget.LinearLayout;

import com.actionbarsherlock.R;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.app.SherlockListActivity;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.internal.widget.CapitalizingButton;
import com.actionbarsherlock.view.MenuItem;

@SuppressLint({ "NewApi" })
public class ActionMenuTextItemView extends LinearLayout implements OnClickListener
{
    private ImageButton mImageButton;
    private CapitalizingButton mTextButton;
    private Object mTarget;
    private MenuItem mItem;

    // Set up all the data. Object must be a sherlock activity or fragment with an onMenuItemSelected().
    public void initialise(MenuItem item, Object target)
    {
        mItem = item;
        mTarget = target;
        setIcon(mItem.getIcon());
        setTitle(mItem.getTitle());
    }

    public ActionMenuTextItemView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }
    public ActionMenuTextItemView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    @Override
    public void onFinishInflate()
    {
        super.onFinishInflate();
        mImageButton = (ImageButton) findViewById(R.id.abs__imageButton);
        mTextButton = (CapitalizingButton) findViewById(R.id.abs__textButton);
        mImageButton.setOnClickListener(this);
        mTextButton.setOnClickListener(this);
        setOnClickListener(this);
    }

    @Override
    public void setEnabled(boolean enabled)
    {
        super.setEnabled(enabled);
        mImageButton.setEnabled(enabled);
        mTextButton.setEnabled(enabled);
    }

    public void setIcon(Drawable icon)
    {
        mImageButton.setImageDrawable(icon);
        if (icon != null)
            mImageButton.setVisibility(VISIBLE);
        else
            mImageButton.setVisibility(GONE);
    }

    public void setTitle(CharSequence title)
    {
        mTextButton.setTextCompat(title);
        setContentDescription(title);
    }

    @Override
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event)
    {
        onPopulateAccessibilityEvent(event);
        return true;
    }

    @Override
    public void onPopulateAccessibilityEvent(AccessibilityEvent event)
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
            super.onPopulateAccessibilityEvent(event);
        final CharSequence cdesc = getContentDescription();
        if (!TextUtils.isEmpty(cdesc))
            event.getText().add(cdesc);
    }

    @Override
    public boolean dispatchHoverEvent(MotionEvent event)
    {
        // Don't allow children to hover; we want this to be treated as a single component.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
            return onHoverEvent(event);
        return false;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int minWidth = 0;

        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int specSize = MeasureSpec.getSize(widthMeasureSpec);
        final int oldMeasuredWidth = getMeasuredWidth();
        final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, minWidth) : minWidth;

        if (widthMode != MeasureSpec.EXACTLY && minWidth > 0 && oldMeasuredWidth < targetWidth)
        {
            // Remeasure at exactly the minimum width.
            super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
        }
    }
    @Override
    public void onClick(View v)
    {
        if (mTarget == null)
            return;
        else if (mTarget instanceof SherlockActivity)
            ((SherlockActivity)mTarget).onOptionsItemSelected(mItem);
        else if (mTarget instanceof SherlockFragmentActivity)
            ((SherlockFragmentActivity)mTarget).onOptionsItemSelected(mItem);
        else if (mTarget instanceof SherlockListActivity)
            ((SherlockListActivity)mTarget).onOptionsItemSelected(mItem);
        else if (mTarget instanceof SherlockListFragment)
            ((SherlockListFragment)mTarget).onOptionsItemSelected(mItem);
        else if (mTarget instanceof SherlockFragment)
            ((SherlockFragment)mTarget).onOptionsItemSelected(mItem);
        else
            throw new IllegalArgumentException("Target must be a sherlock activity or fragment.");
    }

}

Ok now you're ready to use it. In your menu items that you want to have text, you do the same as what dgmltn said:

<item
    android:id="@+id/menu_foo"
    android:icon="@drawable/..."
    android:showAsAction="always|withText" // Doesn't do anything really.
    android:title="Sell"
    android:titleCondensed="Sell"
    android:actionLayout="@layout/view_action_menu_text_item"/> // Or whatever you called it.

And finally, just add this code to your activity/fragment:

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    super.onCreateOptionsMenu(menu);
    getSupportMenuInflater().inflate(R.menu.activity_main, menu);

    // The magic lines.
    MenuItem it = menu.findItem(R.id.menu_foo);
    ((ActionMenuTextItemView)it.getActionView()).initialise(it, this);

And that's it!

Singlehanded answered 24/10, 2012 at 18:9 Comment(0)
D
0
  • Adding to @dgmltn answer
  • For Kotlin users:
  • This is how you access/set listener on item in your Kotlin code.
   override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
       super.onCreateOptionsMenu(menu, inflater)
       menu.clear()
       inflater.inflate(R.menu.my_menu, menu)
       val item = menu.findItem(R.id.menu_foo)
    
       item.actionView!!.setOnClickListener {
           Toast.makeText(
                requireContext(), "hello", Toast.LENGTH_SHORT
            ).show()
        }
    }
Darladarlan answered 12/4, 2023 at 15:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.