How to change PopupMenu items font

7.6k Views Asked by At

I want to change the default font of PopupMenu items and use from my custom font for them.

This is the code that I used for creating PopupMenu :

PopupMenu pm = new PopupMenu(this, v);
getMenuInflater().inflate(R.menu.main, pm.getMenu());
pm.show();

And the menu Items :

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/Setting"
        android:title="Setting"/>
    <item
        android:id="@+id/About"
        android:title="About"/>
    <item
        android:id="@+id/Help"
        android:title="Help"/>
</menu>

I will be so thankful if you share your suggestions with me :-)

Regards

4

There are 4 best solutions below

1
QArea On

I think it's not possible. Actually you can use popupWindow and customize it like menu as you want.

0
Abolhassan Abdolalizade On

you can use reflection. It can be used for any customization of popup menu items. The resource layout of menu item in android support is defined in android.support.v7.internal.view.menu.MenuPopupHelper and it 's field name is "ITEM_LAYOUT" that declared as static final; It 's value equals to "R.layout.abc_popup_menu_item_layout" I find the layout file in Grepcode and copy it to my project layout directory. I named it popup_menu_item_layout.xml. My popup menu item layout comes here

<?xml version="1.0" encoding="utf-8"?>
<mypackage.PopupMenuItemView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?attr/dropdownListPreferredItemHeight"
    android:minWidth="196dip"
    android:paddingRight="16dip">

<!-- Icon will be inserted here. -->

<!-- The title and summary have some gap between them, and this 'group' should be centered vertically. -->
<RelativeLayout
        android:layout_width="0dip"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="16dip"
        android:duplicateParentState="true">

    <TextView
            android:id="@+id/title"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true"
            android:textAppearance="?attr/textAppearanceLargePopupMenu"
            android:singleLine="true"
            android:duplicateParentState="true"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"/>

    <TextView
            android:id="@+id/shortcut"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/title"
            android:layout_alignParentLeft="true"
            android:textAppearance="?attr/textAppearanceSmallPopupMenu"
            android:singleLine="true"
            android:duplicateParentState="true"/>

</RelativeLayout>

<!-- Checkbox, and/or radio button will be inserted here. -->

Then create custom class PopupMenuItemView:

public class PopupMenuItemView extends android.support.v7.internal.view.menu.ListMenuItemView {

public PopupMenuItemView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

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

@Override
protected void onFinishInflate() {
    super.onFinishInflate();

    applyTypefaceToAll(this, your_typeface);
    TypefaceUtils.applyTextSizeToAll(this, your_textsize);
}

public static void applyTypefaceToAll(View view, Typeface typeface) {
    if (view instanceof ViewGroup) {
        ViewGroup viewGroup = (ViewGroup) view;
        for (int childIndex = 0; childIndex < viewGroup.getChildCount(); childIndex++)
            applyTypefaceToAll(viewGroup.getChildAt(childIndex), typeface);
    } else if (view instanceof TextView) {
        TextView textView = (TextView) view;
        textView.setTypeface(typeface);
        textView.setPaintFlags(textView.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
    }
}

public static void applyTextSizeToAll(View view, float size) {
    if (view instanceof ViewGroup) {
        ViewGroup viewGroup = (ViewGroup) view;
        for (int childIndex = 0; childIndex < viewGroup.getChildCount(); childIndex++)
            applyTextSizeToAll(viewGroup.getChildAt(childIndex), size);
    } else if (view instanceof TextView) {
        TextView textView = (TextView) view;
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
        textView.setPaintFlags(textView.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
    }
}
}

Finally replace layout resource id for menu items by reflection; some where like in your main activity onCreate method or in your app onCreate method:

    try {
         setFinalStatic(MenuPopupHelper.class.getDeclaredField("ITEM_LAYOUT"),
                R.layout.popup_menu_item_layout);
    } catch (Exception e) {
        e.printStackTrace();
    }

    public static void setFinalStatic(Field field, Object newValue) throws Exception {
    field.setAccessible(true);

    try {
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    }catch (Exception e) {
        e.printStackTrace();
    }

    field.set(null, newValue);
}
0
Gaurav Gupta On

Check my solution for same issue :

Popup Menu Mehthod :

 private void showEditPopupWindow(Context mContext) {
        PopupMenu popupMenu = new PopupMenu(mContext, view);
        popupMenu.getMenuInflater().inflate(R.menu.YOUR_MENU, popupMenu.getMenu());
        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                if (item.getItemId() == R.id.delete) {
                  //  Do your stuffs;
                } else {
                  //  Do your stuffs
                }
                return true;
            }
        });

        Menu menu = popupMenu.getMenu();
        for (int i = 0; i < menu.size(); i++) {
            MenuItem mi = menu.getItem(i);
            applyFontToMenuItem(mi);
        }

    }

Apply your font in this method , also you can change color of font:

private void applyFontToMenuItem(MenuItem mi) {
        Typeface font = Typeface.createFromAsset(mContext.getAssets(), "fonts/YOUR_FONT.ttf");
        SpannableString mNewTitle = new SpannableString(mi.getTitle());
        mNewTitle.setSpan(new CustomTypeFaceSpan("", font,Color.WHITE), 0, mNewTitle.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        mi.setTitle(mNewTitle);
    }

CustomTypeFaceSpan Class :

    public class CustomTypeFaceSpan extends TypefaceSpan {

    private final Typeface newType;
    private final int mColor;

    public CustomTypeFaceSpan(String family, Typeface type, @ColorInt int color) {

        super(family);
        newType = type;
        mColor = color;
    }

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

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

    @Override
    public int getSpanTypeId() {
        return super.getSpanTypeId();
    }

    @ColorInt
    public int getForegroundColor() {
        return mColor;
    }




    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);
    }
}
0
Rajarshi On

I know this is an old question but still want to answer, if, someone like me, stumbles upon this particular question looking for a definite answer.

Don't know about AppCompat, but was playing with MaterialComponents and found out the api textAppearanceLargePopupMenu. An example, supose you want to apply a particular font for all PopupMenus in your app, then define a style and apply this to your theme:

Example Style:

<style name="AppTheme.TextAppearance.Popup" parent="TextAppearance.MaterialComponents.Caption">
    <item name="fontFamily">@font/opensans_regular</item>
</style>

Example Theme:

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    <!-- All other attrs and styles -->
    <item name="textAppearanceLargePopupMenu">@style/AppTheme.TextAppearance.Popup</item>
</style>