开发者

How to set a font for the Options menu?

When I create an Options Menu the items seem to default to th开发者_StackOverflowe native "sans" font. When I look at commercial apps they mostly seem to do the same thing. Is it possible to set the font size, color weight or typeface for Option Menu items?

Thanks in advance.


You can customize the option menu, including:

  1. Add a custom font

  2. Change font size

  3. Change font color

  4. Set background to a Drawable resource (e.g. image, border, gradient)

To change background to a border or gradient you have to create a resource folder in res called drawable and, inside it, create the border XML or gradient XML.

This can all be done programatically as shown below:

public class CustomMenu extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    public boolean onCreateOptionsMenu(android.view.Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.cool_menu, menu);
        getLayoutInflater().setFactory(new Factory() {
            public View onCreateView(String name, Context context,
                    AttributeSet attrs) {

                if (name.equalsIgnoreCase(
                        "com.android.internal.view.menu.IconMenuItemView")) {
                    try {
                        LayoutInflater li = LayoutInflater.from(context);
                        final View view = li.createView(name, null, attrs);
                        new Handler().post(new Runnable() {
                            public void run() {
                                // set the background drawable if you want that
                                //or keep it default -- either an image, border
                                //gradient, drawable, etc.
                                view.setBackgroundResource(R.drawable.myimage);
                                ((TextView) view).setTextSize(20); 

                                // set the text color
                                Typeface face = Typeface.createFromAsset(
                                        getAssets(),"OldeEnglish.ttf");     
                                ((TextView) view).setTypeface(face);
                                ((TextView) view).setTextColor(Color.RED);
                            }
                        });
                        return view;
                    } catch (InflateException e) {
                        //Handle any inflation exception here
                    } catch (ClassNotFoundException e) {
                        //Handle any ClassNotFoundException here
                    }
                }
                return null;
            }
        });
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.AboutUs:
            Intent i = new Intent("com.test.demo.ABOUT");
            startActivity(i);
            break;
        case R.id.preferences:
            Intent p = new Intent("com.test.demo.PREFS");
            startActivity(p);
            break;
        case R.id.exit:
            finish();
            break;
        }
        return false;
    }
}

Dont forget to create folder called menu in res folder, and inside the menu folder create an XML for your menu (e.g. cool_menu.xml) such as this:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item  android:title="about"android:id="@+id/AboutUs" /> 
    <item android:title="Prefs" android:id="@+id/preferences" /> 
    <item android:title="Exit" android:id="@+id/exit" /> 
</menu>

Then the output will be something like this:

How to set a font for the Options menu?


@Android Stack, when I read your answer I started to panic thinking that I would have to use a "factory".

I searched around for a bit, and I learned that you can use custom views for menu items. Simply call setActionView on the menu item.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    // Inflate the menu items for use in the action bar
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.my_menu, menu);

    // Get the root inflator. 
    LayoutInflater baseInflater = (LayoutInflater)getBaseContext()
           .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    // Inflate your custom view.
    View myCustomView = baseInflater.inflate(R.layout.my_custom_view, null);
    menu.findItem(R.id.my_custom_menu_icon).setActionView(myCustomView);


    // If myCustomView has additional children, you might have to inflate them separately here.
    // In my case, I used buttons in my custom view, and registered onClick listeners at this point.

 }

Your implementation of my_custom_view can be any view you want (although it might have to have a LinearLayout as the root element). For instance, you can use the TextView + ImageView layout that @R4j proposes in his answer.

In my use case, I simply put Button objects into the menu, and then relied on the onButtonClick handler of the buttons to respond to events -- effectively side-stepping the need to handle them in the activity containing the menu.

(Great question, by the way. Thanks!!)


Tested and work like charm :)

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_feedback_filter, menu);

    for (int i = 0; i < menu.size(); i++) {
        MenuItem mi = menu.getItem(i);
        //for aapplying a font to subMenu ...
        SubMenu subMenu = mi.getSubMenu();
        if (subMenu != null && subMenu.size() > 0) {
            for (int j = 0; j < subMenu.size(); j++) {
                MenuItem subMenuItem = subMenu.getItem(j);
                applyFontToMenuItem(subMenuItem, typeface);
            }
        }
        //the method we have create in activity
        applyFontToMenuItem(mi, typeface);
    }

    return super.onCreateOptionsMenu(menu);
}



private void applyFontToMenuItem(MenuItem mi, Typeface font) {
    SpannableString mNewTitle = new SpannableString(mi.getTitle());
    mNewTitle.setSpan(new CustomTypefaceSpan("", font), 0, mNewTitle.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
    mi.setTitle(mNewTitle);
}

Custom span class

import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.TypefaceSpan;

public class CustomTypefaceSpan extends TypefaceSpan {

    private final Typeface newType;

    public CustomTypefaceSpan(String family, Typeface type) {
        super(family);
        newType = type;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        applyCustomTypeFace(ds, newType);
    }

    @Override
    public void updateMeasureState(TextPaint paint) {
        applyCustomTypeFace(paint, newType);
    }

    private static void applyCustomTypeFace(Paint paint, Typeface tf) {
        int oldStyle;
        Typeface old = paint.getTypeface();
        if (old == null) {
            oldStyle = 0;
        } else {
            oldStyle = old.getStyle();
        }

        int fake = oldStyle & ~tf.getStyle();
        if ((fake & Typeface.BOLD) != 0) {
            paint.setFakeBoldText(true);
        }

        if ((fake & Typeface.ITALIC) != 0) {
            paint.setTextSkewX(-0.25f);
        }

        paint.setTypeface(tf);
    }
}


Instead of Using the XML Resource for Menu, inflate it from code using menu.add and use new SpannableString() for assigning a custom font.

Here is an example working on Android 4.x:

@Override
public void onCreateContextMenu(ContextMenu menu, View v,
                                ContextMenu.ContextMenuInfo menuInfo) {
    ...
    menu.add(Menu.NONE,1234,1,wrapInSpan(getResources().getString(R.string.item_title)))
        .setTitleCondensed(getResources().getString(R.string.item_title));
    ...
}

private CharSequence wrapInSpan(CharSequence value) {
    SpannableStringBuilder sb = new SpannableStringBuilder(value);
    sb.setSpan(MY_TYPEFACE, 0, value.length(), 0);
    return sb;
}

The setTitleCondensed(...) is required to workaround a bug in android API : when a menu item is selected the event is logged and it use the titleCondensed to write the log. If the titleCondensed is not defined it use the title and the EventLog.writeEvent crashes whenever the string to log is formatted.

So passing an non-formated CharSequence in the consendedTitle workaround the bug.


None of the answers above worked for me. I achieved this by following solution:

public boolean onPrepareOptionsMenu(Menu menu)
    {
        MenuItem item = menu.findItem(R.id.menu_name);
        item.setTitle(someTextToDisplayOnMenu);
        SpannableString spanString = new SpannableString(item.getTitle().toString());
        spanString.setSpan(new TextAppearanceSpan(context,android.R.style.TextAppearance_Medium), 0,spanString.length(), 0);
        spanString.setSpan(new ForegroundColorSpan(Color.WHITE), 0, spanString.length(), 0); //fix the color to white
        item.setTitle(spanString);
        return true;
    }


I think Android doesn't support customization for option menu. But you can try another way: http://www.codeproject.com/Articles/173121/Android-Menus-My-Way
In this way, actually menu item is a textview and imageview, so you can easily change font, color...

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center"
android:padding="4dip"
android:clickable="true"
android:background="@drawable/custom_menu_selector">
<ImageView
    android:id="@+id/custom_menu_item_icon"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:paddingBottom="2dip"
    android:paddingTop="2dip"/>
<TextView
    android:id="@+id/custom_menu_item_caption"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="#ffffff"
    android:textSize="12sp"
    android:gravity="center"/>


The only solution I found was create a custom dialog what appears when you press the menu button. The layout would be like that:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Mi cuenta"
        android:id="@+id/buttonMyAccount" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Ayuda"
        android:id="@+id/buttonHelp" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Contacto"
        android:id="@+id/buttonContact" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Acerca de"
        android:id="@+id/buttonAbout" />
</LinearLayout>

After that, from Activity class, in the 'OnOptionsItemSelected' method I write the following code:

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {

            case R.id.action_settings:
            Dialog dialog = new Dialog(this);
            dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
            dialog.setContentView(R.layout.options_menu);
            dialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));

            dialog.show();


            Button buttonMyAccount = (Button) dialog.findViewById(R.id.buttonMyAccount);
            Typeface font = Typeface.createFromAsset(this.getAssets(), "SamsungIF_Rg.ttf");
            buttonMyAccount.setTypeface(font);
            buttonMyAccount.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent itMyAccount = new Intent(getBaseContext(), AccountActivity.class);
                    startActivity(itMyAccount);
                }
            });


            Button buttonHelp = (Button) dialog.findViewById(R.id.buttonHelp);
            buttonHelp.setTypeface(font);
            buttonHelp.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent itAssistant = new Intent(getBaseContext(), AssistantPagerActivity.class);
                    startActivity(itAssistant);
                }
            });


            Button buttonContact = (Button) dialog.findViewById(R.id.buttonContact);
            buttonContact.setTypeface(font);
            buttonContact.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent itContact = new Intent(getBaseContext(), ContactActivity.class);
                    startActivity(itContact);
                }
            });

            Button buttonAbout = (Button) dialog.findViewById(R.id.buttonAbout);
            buttonAbout.setTypeface(font);
            buttonAbout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent itAbout = new Intent(getBaseContext(), AboutActivity.class);
                    startActivity(itAbout);
                }
            });


            Window window = dialog.getWindow();
            WindowManager.LayoutParams wlp = window.getAttributes();
            wlp.gravity = Gravity.RIGHT | Gravity.TOP;
            wlp.y = getSupportActionBar().getHeight();
            wlp.width = 300;
            wlp.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND;
            window.setAttributes(wlp);


            return true;

        default:
            return super.onOptionsItemSelected(item);

    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜