我需要在整个应用程序中使用某种特定字体。我有相应的.ttf文件。 是否可以在应用程序启动时将其设置为默认字体,然后在应用程序的其他地方使用它?设置后,我该如何在我的布局XML中使用它?
我需要在整个应用程序中使用某种特定字体。我有相应的.ttf文件。 是否可以在应用程序启动时将其设置为默认字体,然后在应用程序的其他地方使用它?设置后,我该如何在我的布局XML中使用它?
是的,带有反射。这个方法可行 (基于这个答案):
(注意:这是一种绕过自定义字体支持不足的解决办法,因此如果您想改变这种情况,请点亮并赞成此处的Android问题)。注意: 不要在该问题上留下“我也是”评论,每个已观注它的人在您这样做时都会收到一封电子邮件。所以请只是“点亮”它。
import java.lang.reflect.Field;
import android.content.Context;
import android.graphics.Typeface;
public final class FontsOverride {
public static void setDefaultFont(Context context,
String staticTypefaceFieldName, String fontAssetName) {
final Typeface regular = Typeface.createFromAsset(context.getAssets(),
fontAssetName);
replaceFont(staticTypefaceFieldName, regular);
}
protected static void replaceFont(String staticTypefaceFieldName,
final Typeface newTypeface) {
try {
final Field staticField = Typeface.class
.getDeclaredField(staticTypefaceFieldName);
staticField.setAccessible(true);
staticField.set(null, newTypeface);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
接下来您需要重载一些默认字体,例如在应用程序类中:
public final class Application extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");
FontsOverride.setDefaultFont(this, "MONOSPACE", "MyFontAsset2.ttf");
FontsOverride.setDefaultFont(this, "SERIF", "MyFontAsset3.ttf");
FontsOverride.setDefaultFont(this, "SANS_SERIF", "MyFontAsset4.ttf");
}
}
当然,如果您使用相同的字体文件,您可以改进此操作以仅加载一次。
但是,我倾向于只覆盖一个字体,比如"MONOSPACE"
,然后设置一种风格来强制在整个应用程序中应用该字体类型:
<resources>
<style name="AppBaseTheme" parent="android:Theme.Light">
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:typeface">monospace</item>
</style>
</resources>
我已经调查了评论中的报告,发现它似乎与主题 android:Theme.Material.Light
不兼容。
如果您不重视该主题,请使用较旧的主题,例如:
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<item name="android:typeface">monospace</item>
</style>
"DEFAULT_BOLD"
,但也有一个私有字段:private static Typeface[] sDefaults;
您可以尝试通过反射访问它,并将 sDefaults[Typeface.ITALIC]
设置为您的 Typeface。请参见 此链接。 - westonAndroid有一个非常棒的自定义字体库:Calligraphy
这里是如何使用它的示例。
在Gradle中,您需要将以下行放入您的应用程序的build.gradle文件中:
dependencies {
compile 'uk.co.chrisjenx:calligraphy:2.2.0'
}
然后创建一个继承 Application
的类,并编写以下代码:
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
.setDefaultFontPath("your font path")
.setFontAttrId(R.attr.fontPath)
.build()
);
}
}
在活动类中,在onCreate方法之前添加以下方法:
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}
最后,你的清单文件应该像这样:
<application
.
.
.
android:name=".App">
它会将整个活动更改为您的字体! 这很简单、干净!
strings.xml
文件中使用了<b></b>
、<u></u>
和<i></i>
标签,目前看来它可以正常工作。 - jlively虽然这种方法不能用于整个应用程序,但可以用于一个Activity,并且可以在任何其他Activity中重复使用。我已经感谢 @FR073N 的帮助更新了我的代码以支持其他视图。我不确定关于 Buttons
、RadioGroups
等的问题,因为这些类都扩展自 TextView
,所以它们应该能够正常工作。我添加了一个布尔条件来使用反射,因为它似乎非常破坏性,可能会明显影响性能。
注意:正如指出的那样,这对于动态内容是行不通的!对于这种情况,可以通过调用此方法,例如使用 onCreateView
或 getView
方法,但需要额外的努力。
/**
* Recursively sets a {@link Typeface} to all
* {@link TextView}s in a {@link ViewGroup}.
*/
public static final void setAppFont(ViewGroup mContainer, Typeface mFont, boolean reflect)
{
if (mContainer == null || mFont == null) return;
final int mCount = mContainer.getChildCount();
// Loop through all of the children.
for (int i = 0; i < mCount; ++i)
{
final View mChild = mContainer.getChildAt(i);
if (mChild instanceof TextView)
{
// Set the font if it is a TextView.
((TextView) mChild).setTypeface(mFont);
}
else if (mChild instanceof ViewGroup)
{
// Recursively attempt another ViewGroup.
setAppFont((ViewGroup) mChild, mFont);
}
else if (reflect)
{
try {
Method mSetTypeface = mChild.getClass().getMethod("setTypeface", Typeface.class);
mSetTypeface.invoke(mChild, mFont);
} catch (Exception e) { /* Do something... */ }
}
}
}
然后你可以像这样使用它:final Typeface mFont = Typeface.createFromAsset(getAssets(),
"fonts/MyFont.ttf");
final ViewGroup mContainer = (ViewGroup) findViewById(
android.R.id.content).getRootView();
HomeActivity.setAppFont(mContainer, mFont);
希望能帮到你。
总之:
选项#1:使用反射来应用字体(结合weston和Roger Huang的回答):
import java.lang.reflect.Field;
import android.content.Context;
import android.graphics.Typeface;
public final class FontsOverride {
public static void setDefaultFont(Context context,
String staticTypefaceFieldName, String fontAssetName) {
final Typeface regular = Typeface.createFromAsset(context.getAssets(),
fontAssetName);
replaceFont(staticTypefaceFieldName, regular);
}
protected static void replaceFont(String staticTypefaceFieldName,final Typeface newTypeface) {
if (isVersionGreaterOrEqualToLollipop()) {
Map<String, Typeface> newMap = new HashMap<String, Typeface>();
newMap.put("sans-serif", newTypeface);
try {
final Field staticField = Typeface.class.getDeclaredField("sSystemFontMap");
staticField.setAccessible(true);
staticField.set(null, newMap);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
try {
final Field staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName);
staticField.setAccessible(true);
staticField.set(null, newTypeface);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
在 Application 类中的用法:
public final class Application extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");
FontsOverride.setDefaultFont(this, "MONOSPACE", "MyFontAsset2.ttf");
FontsOverride.setDefaultFont(this, "SERIF", "MyFontAsset3.ttf");
FontsOverride.setDefaultFont(this, "SANS_SERIF", "MyFontAsset4.ttf");
}
}
根据lovefish的方法,设置一个样式以强制应用字体类型在整个应用中生效:
Android 5.0以下版本(Pre-Lollipop):
<resources>
<style name="AppBaseTheme" parent="Theme.AppCompat.Light">
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:typeface">monospace</item>
</style>
</resources>
棒棒糖(API 21):
<resources>
<style name="AppBaseTheme" parent="Theme.AppCompat.Light">
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:textAppearance">@style/CustomTextAppearance</item>
</style>
<style name="CustomTextAppearance">
<item name="android:typeface">monospace</item>
</style>
</resources>
选项2:为需要自定义字体的每个视图子类化,即ListView、EditText、Button等。(Palani的答案):
public class CustomFontView extends TextView {
public CustomFontView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CustomFontView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomFontView(Context context) {
super(context);
init();
}
private void init() {
if (!isInEditMode()) {
Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "Futura.ttf");
setTypeface(tf);
}
}
选项3:实现一个视图爬虫来遍历当前屏幕的视图层次结构:
变体#1(Tom的回答):
public static final void setAppFont(ViewGroup mContainer, Typeface mFont, boolean reflect)
{
if (mContainer == null || mFont == null) return;
final int mCount = mContainer.getChildCount();
// Loop through all of the children.
for (int i = 0; i < mCount; ++i)
{
final View mChild = mContainer.getChildAt(i);
if (mChild instanceof TextView)
{
// Set the font if it is a TextView.
((TextView) mChild).setTypeface(mFont);
}
else if (mChild instanceof ViewGroup)
{
// Recursively attempt another ViewGroup.
setAppFont((ViewGroup) mChild, mFont);
}
else if (reflect)
{
try {
Method mSetTypeface = mChild.getClass().getMethod("setTypeface", Typeface.class);
mSetTypeface.invoke(mChild, mFont);
} catch (Exception e) { /* Do something... */ }
}
}
}
使用方法:
final ViewGroup mContainer = (ViewGroup) findViewById(
android.R.id.content).getRootView();
HomeActivity.setAppFont(mContainer, Typeface.createFromAsset(getAssets(),
"fonts/MyFont.ttf"));
变化#2: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere.
选项#4:使用第三方库Calligraphy。
个人建议选择选项#4,因为它可以避免很多麻烦。
newMap.put()
中的sans-serif
和styles
中的monospace
是什么意思?!我想使用一个名为barana.ttf
的自定义字体。 - Dr.jacky我希望你能翻译并改进weston在API 21 Android 5.0中的答案。
在API 21下,大多数文本样式都包含fontFamily设置,例如:
<style name="TextAppearance.Material">
<item name="fontFamily">@string/font_family_body_1_material</item>
</style>
应用默认的Roboto Regular字体:
<string name="font_family_body_1_material">sans-serif</string>
protected static void replaceFont(String staticTypefaceFieldName,
final Typeface newTypeface) {
if (isVersionGreaterOrEqualToLollipop()) {
Map<String, Typeface> newMap = new HashMap<String, Typeface>();
newMap.put("sans-serif", newTypeface);
try {
final Field staticField = Typeface.class
.getDeclaredField("sSystemFontMap");
staticField.setAccessible(true);
staticField.set(null, newMap);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
try {
final Field staticField = Typeface.class
.getDeclaredField(staticTypefaceFieldName);
staticField.setAccessible(true);
staticField.set(null, newTypeface);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");
。我使用的是Nexus 5,版本为5.1.1。 - kip2Build.VERSION.SDK_INT == 21
API 21
。我在API 22的Nexus上测试过。非常简单... 1. 下载并将您的自定义字体放在资产中...然后编写一个单独的文本视图类,如下所示:这里我使用了 Futura 字体
public class CusFntTextView extends TextView {
public CusFntTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public CusFntTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CusFntTextView(Context context) {
super(context);
init();
}
private void init() {
if (!isInEditMode()) {
Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "Futura.ttf");
setTypeface(tf);
}
}
}
并在XML中执行以下操作:
<com.packagename.CusFntTextView
android:id="@+id/tvtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hi Android"
android:textAppearance="?android:attr/textAppearanceLarge"
/>
public FontTextView(Context context) {
super(context);
init();
}
public FontTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FontTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
protected void init() {
setTypeface(Typeface.createFromAsset(getContext().getAssets(), AppConst.FONT));
}
Android 4.4 及以下版本:
<resources>
<style name="AppBaseTheme" parent="Theme.AppCompat.Light">
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:typeface">monospace</item>
</style>
</resources>
超过(等于)API 5.0
<resources>
<style name="AppBaseTheme" parent="Theme.AppCompat.Light">
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:textAppearance">@style/CustomTextAppearance</item>
</style>
<style name="CustomTextAppearance">
<item name="android:typeface">monospace</item>
</style>
</resources>
FontsOverride实用程序文件与weston的答案中所述的相同。 我已在以下手机上进行了测试:
Nexus 5(Android 5.1主要Android系统)
ZTE V5(Android 5.1 CM12.1)
XIAOMI Note(Android 4.4 MIUI6)
HUAWEI C8850(Android 2.3.5未知)
import android.content.Context;
import android.graphics.Typeface;
import android.os.Build;
import android.util.Log;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class FontsOverride {
private static final int BOLD = 1;
private static final int BOLD_ITALIC = 2;
private static final int ITALIC = 3;
private static final int LIGHT = 4;
private static final int CONDENSED = 5;
private static final int THIN = 6;
private static final int MEDIUM = 7;
private static final int REGULAR = 8;
private Context context;
public FontsOverride(Context context) {
this.context = context;
}
public void loadFonts() {
Map<String, Typeface> fontsMap = new HashMap<>();
fontsMap.put("sans-serif", getTypeface("comic.ttf", REGULAR));
fontsMap.put("sans-serif-bold", getTypeface("comic.ttf", BOLD));
fontsMap.put("sans-serif-italic", getTypeface("comic.ttf", ITALIC));
fontsMap.put("sans-serif-light", getTypeface("comic.ttf", LIGHT));
fontsMap.put("sans-serif-condensed", getTypeface("comic.ttf", CONDENSED));
fontsMap.put("sans-serif-thin", getTypeface("comic.ttf", THIN));
fontsMap.put("sans-serif-medium", getTypeface("comic.ttf", MEDIUM));
overrideFonts(fontsMap);
}
private void overrideFonts(Map<String, Typeface> typefaces) {
if (Build.VERSION.SDK_INT == 21) {
try {
final Field field = Typeface.class.getDeclaredField("sSystemFontMap");
field.setAccessible(true);
Map<String, Typeface> oldFonts = (Map<String, Typeface>) field.get(null);
if (oldFonts != null) {
oldFonts.putAll(typefaces);
} else {
oldFonts = typefaces;
}
field.set(null, oldFonts);
field.setAccessible(false);
} catch (Exception e) {
Log.e("TypefaceUtil", "Cannot set custom fonts");
}
} else {
try {
for (Map.Entry<String, Typeface> entry : typefaces.entrySet()) {
final Field staticField = Typeface.class.getDeclaredField(entry.getKey());
staticField.setAccessible(true);
staticField.set(null, entry.getValue());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
private Typeface getTypeface(String fontFileName, int fontType) {
final Typeface tf = Typeface.createFromAsset(context.getAssets(), "fonts/" + fontFileName);
return Typeface.create(tf, fontType);
}
}
new FontsOverride(this).loadFonts();
在Xamarin.Android工作:
类:
public class FontsOverride
{
public static void SetDefaultFont(Context context, string staticTypefaceFieldName, string fontAssetName)
{
Typeface regular = Typeface.CreateFromAsset(context.Assets, fontAssetName);
ReplaceFont(staticTypefaceFieldName, regular);
}
protected static void ReplaceFont(string staticTypefaceFieldName, Typeface newTypeface)
{
try
{
Field staticField = ((Java.Lang.Object)(newTypeface)).Class.GetDeclaredField(staticTypefaceFieldName);
staticField.Accessible = true;
staticField.Set(null, newTypeface);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
应用程序实施:
namespace SomeAndroidApplication
{
[Application]
public class App : Application
{
public App()
{
}
public App(IntPtr handle, JniHandleOwnership transfer)
: base(handle, transfer)
{
}
public override void OnCreate()
{
base.OnCreate();
FontsOverride.SetDefaultFont(this, "MONOSPACE", "fonts/Roboto-Light.ttf");
}
}
}
样式:
<style name="Theme.Storehouse" parent="Theme.Sherlock">
<item name="android:typeface">monospace</item>
</style>