android官网 - 如何在Android中为整个应用程序设置自定义字体?




android开发 os (9)

是否可以在Android应用程序中设置自定义字体?

我尝试了here发布的内容,但我不知道我的extends Application类在哪里...

有帮助吗?

编辑:

我尝试了以下方法:

  • 添加资源文件夹并在其中插入字体,如下所示:

  • 添加一个从Application扩展的新类

  • 从我的AndroidManifest.xml调用这个新类。

  • 我按照我的风格添加了它。

MyApp.java:

public class MyApp extends Application {
  @Override
  public void onCreate() {
     super.onCreate();
    FontsOverride.setDefaultFont(this, "DEFAULT", "raleway_regular.ttf");
    //  This FontsOverride comes from the example I posted above
  }
  }

AndroidManifest.xml中:

<application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:supportsRtl="true"
      android:name=".MyApp"
      android:theme="@style/AppTheme">
     ....

styles.xml:

 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:fontFamily">default</item>
 </style>

但我的字体仍然没有变换......任何想法?

然后调用MyApp类。 但对我的字体没有影响......

EDIT2:我意识到在为按钮设置自定义样式后,我的按钮会应用自定义字体。 这是我的自定义按钮样式:

<style name="MyButtonStyle" parent="Widget.AppCompat.Button">
    <item name="textAllCaps">false</item>
    <item name="android:textAllCaps">false</item>
</style>

以下是它现在的样子:

所以:我的按钮是应用样式,而不是TextView 。 有关为什么我的自定义字体没有应用于应用程序中的所有项目的任何想法?


Answers

您现在可以使用Android SDK,就像在Android / 2017中发布的Android Developer官方youtube频道一样。

需要API级别26(Android 8 Oreo)及更高版本。

您只需要将您的字体文件放在res/font文件夹中,并在TextView中引用它们,以防您需要具有android:fontFamily属性的特定文件。

如果您想在整个应用程序上使用基本字体,只需输入即可

<item name="android:fontFamily">@font/yourfontname</item> 

styles.xml

如果您愿意,也可以从Android Studio下载自定义字体。 所有这些都可以在上面的视频中找到详细信息。


写一堂课

public class MyApp extends Application{
// Put the onCreate code as you obtained from the post link you reffered
}

现在接下来的事情是在AndroidManifest.xml中为应用程序标记提供应用程序类的名称。 在这种情况下,它是MyApp

<application
android:name=".MyApp"
...
>
...
</application>

因此,每当打开App时,都会调用MyApp类的onCreate方法,并设置字体。

更新将字体文件置于assets / fonts / your_font_file.ttf下

将此行放在应用程序类的onCreate方法下(MyApp)

TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/your_font_file.ttf");

TypefaceUtil的源文件

public class TypefaceUtil {

    /**
     * Using reflection to override default typeface
     * NOTICE: DO NOT FORGET TO SET TYPEFACE FOR APP THEME AS DEFAULT TYPEFACE WHICH WILL BE OVERRIDDEN
     *
     * @param context                    to work with assets
     * @param defaultFontNameToOverride  for example "monospace"
     * @param customFontFileNameInAssets file name of the font from assets
     */
    public static void overrideFont(Context context, String defaultFontNameToOverride, String customFontFileNameInAssets) {

        final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Map<String, Typeface> newMap = new HashMap<String, Typeface>();
            newMap.put("serif", customFontTypeface);
            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 defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
                defaultFontTypefaceField.setAccessible(true);
                defaultFontTypefaceField.set(null, customFontTypeface);
            } catch (Exception e) {
                Log.e(TypefaceUtil.class.getSimpleName(), "Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
            }
        }
    }
}

现在更新style.xml文件

将下面的行放在清单文件中为您的活动包含的样式

  <item name="android:typeface">serif</item>

希望这可以帮助


好像你在styles.xml中使用android:fontFamily而不是android:typeface

尝试更换

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:fontFamily">default</item>
</style>

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:typeface">default</item>
</style>

在其中的main folder.add fonts文件夹中创建资源文件夹。 在fonts文件夹中添加.ttf文件。

在您的应用主题中添加以下内容

 <item name="android:fontFamily">@font/roboto_regular</item>
    <item name="fontFamily">@font/roboto_regular</item>

将类创建为TypefaceUtil

 import android.content.Context;
import android.graphics.Typeface;
import android.util.Log;

import java.lang.reflect.Field;

public class TypefaceUtil {

    /**
     * Using reflection to override default typeface
     * NOTICE: DO NOT FORGET TO SET TYPEFACE FOR APP THEME AS DEFAULT TYPEFACE WHICH WILL BE OVERRIDDEN
     * @param context to work with assets
     * @param defaultFontNameToOverride for example "monospace"
     * @param customFontFileNameInAssets file name of the font from assets
     */
    public static void overrideFont(Context context, String defaultFontNameToOverride, String customFontFileNameInAssets) {
        try {
            final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);

            final Field defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
            defaultFontTypefaceField.setAccessible(true);
            defaultFontTypefaceField.set(null, customFontTypeface);
        } catch (Exception e) {
            Log.e("Can not set","Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
        }
    }
}

在应用程序类或Launcher活动中调用它

        TypefaceUtil.overrideFont(getApplicationContext(), "fonts/roboto_regular.ttf", "fonts/roboto_regular.ttf"); // font from assets: "assets/fonts/Roboto-Regular.ttf

首先,创建一个新类,覆盖您要自定义的任何View。 (例如,想要一个带有自定义字体的Button ?扩展Button )。 例如:

public class CustomButton extends Button {
    private final static int ROBOTO = 0;
    private final static int ROBOTO_CONDENSED = 1;

    public CustomButton(Context context) {
        super(context);
    }

    public CustomButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        parseAttributes(context, attrs); //I'll explain this method later
    }

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

现在,如果您没有,请在res/values/attrs.xml下添加XML文档,并添加:

<resources>
    <!-- Define the values for the attribute -->
    <attr name="typeface" format="enum">
        <enum name="roboto" value="0"/>
        <enum name="robotoCondensed" value="1"/>
    </attr>

    <!-- Tell Android that the class "CustomButton" can be styled, 
         and which attributes it supports -->
    <declare-styleable name="CustomButton">
        <attr name="typeface"/>
    </declare-styleable>
</resources>

好的,所以在这之前,让我们回到之前的parseAttributes()方法:

private void parseAttributes(Context context, AttributeSet attrs) {
    TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.CustomButton);

    //The value 0 is a default, but shouldn't ever be used since the attr is an enum
    int typeface = values.getInt(R.styleable.CustomButton_typeface, 0);

    switch(typeface) {
        case ROBOTO: default:
            //You can instantiate your typeface anywhere, I would suggest as a 
            //singleton somewhere to avoid unnecessary copies
            setTypeface(roboto); 
            break;
        case ROBOTO_CONDENSED:
            setTypeface(robotoCondensed);
            break;
    }

    values.recycle();
}

现在你已经准备好了。 您可以为任何事物添加更多属性(您可以为typefaceStyle添加另一个属性 - 粗体,斜体等),但现在让我们看看如何使用它:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res/com.yourpackage.name"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.yourpackage.name.CustomButton
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me!"
        custom:typeface="roboto" />

</LinearLayout>

xmlns:custom行可以是任何东西,但约定就是如上所示。 重要的是它是独一无二的,这就是使用包名的原因。 现在你只需要为你的属性使用custom:前缀,为android属性使用android:前缀。

最后一件事:如果你想在一个样式( res/values/styles.xml )中使用它,你不应该添加xmlns:custom行。 只需引用没有前缀的属性名称:

<style name="MyStyle>
    <item name="typeface">roboto</item>
</style>

如果你按照第一个主题,这应该工作: Here

是的反思。 这是有效的( 根据这个答案 ):

(注意:由于缺乏对自定义字体的支持,这是一种解决方法,所以如果你想改变这种情况,请在这里做明星向上投票安卓问题)。 注意:不要在这个问题上留下“我也是”的评论,每个看过它的人都会收到一封电子邮件。 所以请“明星”吧。

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>

API 21 Android 5.0

我已经在评论中调查了它不起作用的报告,它似乎与主题android:Theme.Material.Light不兼容。

如果该主题对您不重要,请使用较旧的主题,例如:

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:typeface">monospace</item>
</style>

我也尝试过字体覆盖,但最后它不是一个可靠的解决方案,遗憾的是覆盖字体需要更多。 您可以等待带有自定义字体的Android O出来或使用第三方库。

我遇到的最后一个解决方案是这个库Caligraphy ,它很容易启动,让你可以使用Caligraphy字体。 在检查其源代码的同时,我理解为什么只是覆盖字体不起作用,所以即使你不打算使用它我也建议一次阅读它。

祝好运


在android中有一个很棒的自定义字体库:书法
以下是如何使用它的示例。

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

您应该在资产上创建一个“新目录”“字体”(见下文),因此在该代码中“您的字体路径”应为“fonts / SourceSansPro-Regular.ttf”。 (它只是“字体......”而不是“/ fonts ..”或“assets ..”)

并且在activity类中将此方法放在onCreate之前:

        @Override
        protected void attachBaseContext(Context newBase) {
            super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
        }

你的清单文件应该是这样的最后一件事:

        <application
           .
           .
           .
           android:name=".App">

它会将整个活动改为你的字体! 它简单而干净!

在资产上,您应该右键单击“新建目录”,将其命名为“fonts”。 在finder中放入.ttf字体文件。

也不要忘记在attrs.xml中添加以下两行,如果你没有attrs.xml文件,在values.xml中创建新文件

 <attr format="string" name="fontPath"/> 
    <item name="calligraphy_tag_id" type="id"/>

这是一项正在进行的工作,但这是我不明白的:

ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);

Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );

List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();

Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
    pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
}

Collection<Integer> keys = pidMap.keySet();

for(int key : keys)
{
    int pids[] = new int[1];
    pids[0] = key;
    android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(pids);
    for(android.os.Debug.MemoryInfo pidMemoryInfo: memoryInfoArray)
    {
        Log.i(TAG, String.format("** MEMINFO in pid %d [%s] **\n",pids[0],pidMap.get(pids[0])));
        Log.i(TAG, " pidMemoryInfo.getTotalPrivateDirty(): " + pidMemoryInfo.getTotalPrivateDirty() + "\n");
        Log.i(TAG, " pidMemoryInfo.getTotalPss(): " + pidMemoryInfo.getTotalPss() + "\n");
        Log.i(TAG, " pidMemoryInfo.getTotalSharedDirty(): " + pidMemoryInfo.getTotalSharedDirty() + "\n");
    }
}

为什么不将PID映射到activityManager.getProcessMemoryInfo()中的结果? 很明显,你想让结果数据有意义,所以为什么谷歌很难将结果关联起来呢? 如果我想处理整个内存使用情况,目前的系统甚至不能正常工作,因为返回的结果是一个android.os.Debug.MemoryInfo对象数组,但是这些对象都没有告诉你它们与哪些pid关联。 如果你只是传入所有pid的数组,你将无法理解结果。 据我了解这是使用,它使一次传入多个pid没有意义,然后如果是这样,为什么要使activityManager.getProcessMemoryInfo()只接受一个int数组?





android