How to change PopupMenu items font

2020-08-11 10:18发布

问题:

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

回答1:

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);
    }
}


回答2:

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>


回答3:

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);
}


回答4:

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