【问题标题】:How to set a font for the Options menu?如何为选项菜单设置字体?
【发布时间】:2017-08-12 01:15:39
【问题描述】:

当我创建选项菜单时,项目似乎默认为原生“sans”字体。当我查看商业应用程序时,它们似乎大多都在做同样的事情。是否可以为选项菜单项设置字体大小、颜色粗细或字体?

提前致谢。

【问题讨论】:

标签: android


【解决方案1】:

您可以自定义选项菜单,包括:

  1. 添加自定义字体

  2. 更改字体大小

  3. 更改字体颜色

  4. 将背景设置为可绘制资源(例如图像、边框、渐变)

要将背景更改为边框或渐变,您必须在 res 中创建一个名为 drawable 的资源文件夹,并在其中创建边框 XML 或渐变 XML。

这一切都可以通过编程方式完成,如下所示:

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

不要忘记在res 文件夹中创建名为menu 的文件夹,并在menu 文件夹中为您的菜单创建一个XML(例如cool_menu.xml),例如:

<?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>

那么输出会是这样的:

【讨论】:

  • 嗨,当我运行上面的代码时(稍作修改,见下面的评论),菜单项的外观只有在我第一次打开选项菜单时才会改变。如果我退出选项菜单(通过选择一个选项、按后退按钮或按菜单按钮)然后再次打开它,它的外观将恢复为默认外观。如何使修改持久化?我正在为 API 级别 8 开发。
  • @AndroidStack 抱歉回复晚了。我更改了文本大小和文本颜色,从而生成了一个选项菜单,其中选项中的文本是红色的,并且比平常更大。我第二次调用选项菜单时,文本又是白色的并且是默认大小(不知道确切的大小,但严格来说小于 20)。
  • 它对我不起作用,我得到下一个异常:已在此 LayoutInflater 上设置了工厂
  • 对我来说也是如此 - “已在此 LayoutInflater 上设置了工厂”
  • 有人找到解决方案了吗??
【解决方案2】:

@Android Stack,当我读到你的回答时,我开始恐慌,以为我必须使用“工厂”。

我四处搜索了一下,了解到您可以对菜单项使用自定义视图。只需在菜单项上调用setActionView

@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.

 }

my_custom_view 的实现可以是您想要的任何视图(尽管它可能必须有一个 LinearLayout 作为根元素)。例如,您可以使用@R4j 在他的回答中提出的 TextView + ImageView 布局。

在我的用例中,我只是将Button 对象放入菜单中,然后依靠按钮的onButtonClick 处理程序来响应事件——有效地回避了在包含的活动中处理它们的需要菜单。

(顺便说一句,好问题。谢谢!!)

【讨论】:

  • 我想在这个答案中补充一点,当我们改变菜单上的视图时,当用户点击菜单项时不会调用 onOptionsItemSelected 回调,所以我们必须向我们的 customView 添加一个 onClickListener。
【解决方案3】:

经过测试并像魅力一样工作:)

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

自定义跨度类

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

【讨论】:

  • 它适用于溢出菜单,但不适用于操作菜单。我错过了什么吗?
【解决方案4】:

不要使用菜单的 XML 资源,而是使用 menu.add 从代码中对其进行扩充,并使用 new SpannableString() 来分配自定义字体。

这是一个在 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;
}

setTitleCondensed(...)是在Android API中解决错误所必需的:选择菜单项时,已记录了事件,并且使用 titleCondensed来编写日志。如果titleCondensed 未定义,则它使用titleEventLog.writeEvent 崩溃,只要记录的字符串被格式化。

因此,在 consendedTitle 中传递非格式化的 CharSequence 以解决该错误。

【讨论】:

  • 这在 Menu 保持为 showAsAction="always" 时不起作用。谁能用这段代码解决这个问题?
【解决方案5】:

以上答案都不适合我。 我通过以下解决方案实现了这一点:

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

【讨论】:

    【解决方案6】:

    我认为 Android 不支持自定义选项菜单。但你可以尝试另一种方式:http://www.codeproject.com/Articles/173121/Android-Menus-My-Way
    这样,实际上menu item就是一个textview和imageview,所以你可以很方便的改变字体、颜色...

    <?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"/>
    

    【讨论】:

      【解决方案7】:

      我找到的唯一解决方案是创建一个自定义对话框,当您按下菜单按钮时会出现该对话框。 布局是这样的:

      <?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>
      

      之后,从 Activity 类中,在 'OnOptionsItemSelected' 方法中,我编写了以下代码:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-28
        • 1970-01-01
        • 2018-08-12
        • 2017-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多