Можно ли установить собственный шрифт для всего приложения?


270

Мне нужно использовать определенный шрифт для всего моего приложения. У меня есть файл .ttf для того же. Можно ли установить его в качестве шрифта по умолчанию при запуске приложения, а затем использовать его в другом месте приложения? Когда установлено, как я могу использовать его в моем XML макета?


в Android Studio 3.0 вы можете легко установить пользовательские шрифты: developer.android.com/guide/topics/ui/look-and-feel/…
MHSFisher

@MHSFisher Fonts в XML - это функция для Android 8.0 (уровень API 26)
Emi Raz

1
@EmiRaz, пожалуйста, прочтите документ developer.android.com/guide/topics/ui/look-and-feel/… . эта функция добавлена ​​в библиотеку поддержки 26, но поддерживается из API 16.
MHSFisher

Ответы:


450

Да с отражением. Это работает ( основываясь на этом ответе ):

(Примечание: это обходной путь из-за отсутствия поддержки пользовательских шрифтов, поэтому, если вы хотите изменить эту ситуацию, пожалуйста, отметьте звездочку, чтобы проголосовать за проблему 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>

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>

11
как бы вы переопределили моноширинный шрифт или курсив?
Кристофер Ривера

2
@ChristopherRivera Существует "DEFAULT_BOLD"также частное поле: private static Typeface[] sDefaults;вы можете попробовать получить к нему доступ путем отражения и настройки sDefaults[Typeface.ITALIC]yourTypeface. Смотрите это.
Уэстон

5
@ Weston Не бери в голову. Я достиг этого. В моем понимании произошла ошибка. Вы сделали большую работу. Вы можете просто сказать мне одну вещь? Почему это не работает для гарнитуры DEFAULT? В качестве вашего предложения я изменил гарнитуру на MONOSPACE, а затем применил логин. Это сработало. Но не работает на DEFAULT
Jay Pandya

2
Родной шрифт не может быть ошибочным. Исправлено, добавляя путь шрифтов. "FontsOverride.setDefaultFont (this," DEFAULT "," fonts / MyFontAsset.ttf ");"
Сай

3
Меня действительно раздражает необходимость TextViewвезде менять настройки по умолчанию, чтобы использовать пользовательские шрифты или использовать решение на основе отражений, подобное этому. В сочетании с ограниченным набором шрифтов, доступных в Android, это делает разработку хорошо выглядящих приложений сложной задачей. Я добавил запрос на функцию отслеживания ошибок Android. Если вы хотите увидеть эту функцию, вы можете отметить
Сэм

64

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

и в классе действия поместите этот метод перед onCreate:

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

и последнее, что ваш файл манифеста должен выглядеть следующим образом:

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

и это изменит всю активность на ваш шрифт! это просто и чисто!


1
Это идеальное решение.
nizam.sp

1
Правда, это очень идеальное решение, так что мне не нужно писать разные классы для обновления шрифтов для пунктов меню, переключателей или флажков. Это относится ко всем !! спасибо :)
Маниша

2
Определенно не идеальное решение: жирный / курсив / жирный курсив перезаписаны. В настоящее время нет простого способа установить эти семьи.
0101100101

2
Если я это сделаю Как я могу перейти с Bold на Regular?
GMX

2
Чтобы получить полужирный, курсив или подчеркивание функцию, я использовал <b></b>, <u></u>и <i></i>в strings.xmlфайле и до сих пор работает.
jlively

47

Хотя это не будет работать для всего приложения, оно будет работать для любого действия и может быть повторно использовано для любого другого действия. Я обновил свой код благодаря @ 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);

Надеюсь, это поможет.


4
Я думаю, что лучшим способом было бы переопределить просмотр текста. Этот метод может стать несколько избыточным, если у вас есть приложение с большим количеством просмотров.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
Это может ... но в соответствии со спецификациями Android вы хотите избежать макетов глубже, чем 4 уровня и 100 просмотров в ширину (даже это совсем немного). Рекурсия может быть плохой с точки зрения производительности, но даже если ваш макет имеет 20 уровней, это даже не важно.
Том

Я бы согласился, что это не оказывает заметного влияния на производительность, и я не пытаюсь сказать, что ваш ответ неверен. Мне просто не нравится перебирать каждый вид в макете, если мне это не нужно. Кроме того, расширяя TextView, если вы хотите изменить внешний вид другим способом, вам нужно будет изменить код только в одном месте.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
Просто хочу добавить, что приведенный выше код охватывает только TextViews, но ListViews, Alerts, Toasts, Map Markers и т. Д. По-прежнему будут использовать системный шрифт.
csch

2
ребята, эта функция принимает 3 параметра, когда вы вызываете ее рекурсивно, вы передали 2 параметра ??? какой правильный? что такое отражать ??
MBH

34

В итоге:

Вариант № 1: Используйте отражение, чтобы применить шрифт (объединяя ответ Уэстона и Роджера Хуанга ):

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

} 

Использование в классе приложения:

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 ):

Pre-леденец:

<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, EditTextView, Button и т. Д. ( Ответ Палани ):

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. Реализуйте View Crawler, который просматривает иерархию представлений вашего текущего экрана:

Вариант № 1 ( ответ Тома ):

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: Используйте сторонний Lib под названием Каллиграфия .

Лично я бы порекомендовал вариант № 4, так как он избавляет от многих головных болей.


В Option # 1 , что вы имеете в виду "sans-serif"в newMap.put(и monospaceв styles?! Я хочу использовать собственный шрифт с именем barana.ttf.
Dr.jacky

в варианте 1 первый фрагмент кода не работает с API уровня 22, Android 5.1.1. Но другой раздел кода делает. Какое должно быть точное условие?
user1510006

28

Я хотел бы улучшить ответ Вестона для 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>

Исходный ответ не может применить моноширинный шрифт, потому что android: fontFamily имеет больший приоритет по сравнению с атрибутом android: typeface ( ссылка ). Использование Theme.Holo. * - допустимый обходной путь, потому что внутри нет настроек android: fontFamily.

Решение

Поскольку Android 5.0 помещает системную гарнитуру в статическую переменную Typeface.sSystemFontMap ( reference ), мы можем использовать ту же технику отражения для ее замены:

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

4
хороший обходной путь для Л. однако мне интересно, почему это не работает для моих Buttonназваний и панелей инструментов. Я переопределение шрифта по умолчанию в MainApplication следующим образом : FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. Я использую Nexus 5, v5.1.1
kip2

3
Эй, спасибо. Это было очень полезно, только одно: это не работает на Android 6 (API 22). Тогда код должен быть изменен на это: Build.VERSION.SDK_INT == 21- Это как раз в API 21 I тесте на Nexus с API 22
Saeid

Тем не менее я не могу установить пользовательский шрифт, используя этот метод в Nexus 9
Rahul Upadhyay

2
Любой, у кого проблема не работает для 5.1+. Не переопределяйте гарнитуру в стилях values-v21.
Мухаммед Бабар

@MuhammadBabar Спасибо, ваше решение сделало мой день
M.Yogeshwaran

15

это очень просто ... 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"
      />

Да, это работает :) Но что, если я хочу применить те же шрифты для других оставшихся видов / виджетов? Нужно ли мне писать отдельный класс для всех других представлений (включая диалоги / тосты / панели действий)?
Нарендра Сингх

Да, для этого решения вам нужно написать отдельные классы для каждого вашего представления.
Саад Билал

9

Я также предложил бы расширить TextView и другие элементы управления, но было бы лучше рассмотреть возможность установки шрифта в конструкциях.

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

2
Будьте осторожны, когда делаете это на платформах до 4.0 - это приведет к утечке большого количества ресурсов из-за ошибки в Android: code.google.com/p/android/issues/detail?id=9904
Райан М,

Как мы можем вернуться к умолчанию. Я делаю что-то вроде этого: if (configShared.getString ("storeId", AppCredentials.DEFAULT_STORE_ID) .equals ("2")) {final Field staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (истина); staticField.set (null, newTypeface); } else {final Field staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (истина); staticField.set (null, null); }
Убийца

8

Я хотел бы улучшить ответы Уэстона и Роджера Хуанга для Android 21 над леденцом на палочке с темой " Theme.AppCompat “.

Ниже 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 ответ «s. Я проверил в этих телефонах:

Nexus 5 (Android 5.1 Основная система Android)

ZTE V5 (Android 5.1 CM12.1)

Примечание XIAOMI (Android 4.4 MIUI6)

HUAWEI C8850 (Android 2.3.5 НЕИЗВЕСТНО)


8

Отличное решение можно найти здесь: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Просто расширьте действия из BaseActivity и напишите эти методы. Также вы должны лучше кэшировать шрифты, как описано здесь: https://stackoverflow.com/a/16902532/2914140 .


После некоторых исследований я написал код, который работает на Samsung Galaxy Tab A (Android 5.0). Использовали код Уэстона и Роджера Хуанга, а также https://stackoverflow.com/a/33236102/2914140 . Также протестирован на Lenovo TAB 2 A10-70L, где он не работает. Я вставил здесь шрифт Comic Sans, чтобы увидеть разницу.

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

Чтобы запустить код во всем приложении, вы должны написать в некотором классе, таком как Application, следующее:

    new FontsOverride(this).loadFonts();

Создайте папку «шрифты» внутри «ресурсов» и поместите туда нужные шрифты. Простая инструкция может быть найдена здесь: https://stackoverflow.com/a/31697103/2914140 .

Устройство Lenovo также неправильно получает значение гарнитуры. В большинстве случаев он возвращает Typeface.NORMAL, иногда ноль. Даже если TextView выделен жирным шрифтом (в макете XML-файла). Смотрите здесь: TextView isBold всегда возвращает NORMAL . Таким образом, текст на экране всегда написан обычным шрифтом, а не жирным шрифтом или курсивом. Так что я думаю, что это ошибка производителя.


Я изучил исходный код Android, ваш ответ является лучшим лучшим. Он может заменить тонкий, средний, легкий или что-нибудь еще. Спасибо вам большое!
Мухаммед Омидвар

6

Работаем на 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>

6

Что касается Android O, теперь это можно определить непосредственно из XML, и моя ошибка теперь закрыта!

Смотрите здесь для деталей

TL; DR:

Сначала вы должны добавить свои шрифты в проект

Во-вторых, вы добавляете семейство шрифтов, например так:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
    <font
        android:fontStyle="normal"
        android:fontWeight="400"
        android:font="@font/lobster_regular" />
    <font
        android:fontStyle="italic"
        android:fontWeight="400"
        android:font="@font/lobster_italic" />
</font-family>

Наконец, вы можете использовать шрифт в макете или стиле:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/lobster"/>

<style name="customfontstyle" parent="@android:style/TextAppearance.Small">
    <item name="android:fontFamily">@font/lobster</item>
</style>

Наслаждайтесь!


Спасибо. Мы должны использовать Android Studio 2.4, чтобы добавить папку шрифтов.
Фотон Поинт

на самом деле мне просто нужно применить его к моему стилю, и он будет применяться через приложение. но некоторые (программно добавленные элементы управления должны будут применяться через код Java)
SolidSnake

4

Вы можете установить пользовательские шрифты для каждого макета один за другим, с помощью всего одного вызова функции из каждого макета, передавая его корневой View.First, создать единый подход для доступа к объекту шрифта, как этот

 public class Font {
    private static Font font;
    public Typeface ROBO_LIGHT;

    private Font() {

    }

    public static Font getInstance(Context context) {
        if (font == null) {
            font = new Font();
            font.init(context);
        }
        return font;

    }

    public void init(Context context) {

        ROBO_LIGHT = Typeface.createFromAsset(context.getAssets(),
                "Roboto-Light.ttf");
    }

}

Вы можете определить различные шрифты в вышеприведенном классе. Теперь определите класс Helper для шрифтов, который будет применять шрифты:

   public class FontHelper {

    private static Font font;

    public static void applyFont(View parentView, Context context) {

        font = Font.getInstance(context);

        apply((ViewGroup)parentView);

    }

    private static void apply(ViewGroup parentView) {
        for (int i = 0; i < parentView.getChildCount(); i++) {

            View view = parentView.getChildAt(i);

//You can add any view element here on which you want to apply font 

            if (view instanceof EditText) {

                ((EditText) view).setTypeface(font.ROBO_LIGHT);

            }
            if (view instanceof TextView) {

                ((TextView) view).setTypeface(font.ROBO_LIGHT);

            }

            else if (view instanceof ViewGroup
                    && ((ViewGroup) view).getChildCount() > 0) {
                apply((ViewGroup) view);
            }

        }

    }

}

В приведенном выше коде я применяю шрифты только к textView и EditText, вы также можете применять шрифты к другим элементам представления аналогично. Вам просто нужно передать идентификатор вашей корневой группы представления вышеописанному методу применения шрифта. например, ваш макет:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/mainParent"
    tools:context="${relativePackage}.${activityClass}" >

    <RelativeLayout
        android:id="@+id/mainContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/homeFooter"
        android:layout_below="@+id/edit" >

        <ImageView
            android:id="@+id/PreviewImg"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/abc_list_longpressed_holo"
            android:visibility="gone" />

        <RelativeLayout
            android:id="@+id/visibilityLayer"
            android:layout_width="match_parent"
            android:layout_height="fill_parent" >

            <ImageView
                android:id="@+id/UseCamera"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_centerHorizontal="true"
                android:src="@drawable/camera" />

            <TextView
                android:id="@+id/tvOR"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/UseCamera"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:text="OR"
                android:textSize="30dp" />

            <TextView
                android:id="@+id/tvAND"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="20dp"
                android:text="OR"
                android:textSize="30dp" />

</RelativeLayout>

В вышеприведенном макете идентификатор родительского элемента «Основной родитель» теперь позволяет применять шрифт

public class MainActivity extends BaseFragmentActivity {

    private EditText etName;
    private EditText etPassword;
    private TextView tvTitle;
    public static boolean isHome = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

       Font font=Font.getInstance(getApplicationContext());
        FontHelper.applyFont(findViewById(R.id.mainParent),          getApplicationContext());
   }    
}

Ура :)


Хорошо .. больше нет того же самого шрифта, создайте заново, только 1 шрифт для всего поля. :)
Арфан Мирза

3

Я бы предложил расширить TextView и всегда использовать ваш собственный TextView в макетах XML или там, где вам нужен TextView. В вашем пользовательском TextView переопределитеsetTypeface

@Override
public void setTypeface(Typeface tf, int style) {
    //to handle bold, you could also handle italic or other styles here as well
    if (style == 1){
        tf = Typeface.createFromAsset(getContext().getApplicationContext().getAssets(), "MuseoSans700.otf");
    }else{
        tf = Typeface.createFromAsset(getContext().getApplicationContext().getAssets(), "MuseoSans500.otf");
    }
    super.setTypeface(tf, 0);
}

2

Решение Тома прекрасно работает, но работает только с TextView и EditText.

Если вы хотите охватить большинство представлений (RadioGroup, TextView, Checkbox ...), я создал метод, который делает это:

protected void changeChildrenFont(ViewGroup v, Typeface font){
    for(int i = 0; i < v.getChildCount(); i++){

        // For the ViewGroup, we'll have to use recursivity
        if(v.getChildAt(i) instanceof ViewGroup){
            changeChildrenFont((ViewGroup) v.getChildAt(i), font);
        }
        else{
            try {
                Object[] nullArgs = null;
                //Test wether setTypeface and getTypeface methods exists
                Method methodTypeFace = v.getChildAt(i).getClass().getMethod("setTypeface", new Class[] {Typeface.class, Integer.TYPE});
                //With getTypefaca we'll get back the style (Bold, Italic...) set in XML
                Method methodGetTypeFace = v.getChildAt(i).getClass().getMethod("getTypeface", new Class[] {});
                Typeface typeFace = ((Typeface)methodGetTypeFace.invoke(v.getChildAt(i), nullArgs));
                //Invoke the method and apply the new font with the defined style to the view if the method exists (textview,...)
                methodTypeFace.invoke(v.getChildAt(i), new Object[] {font, typeFace == null ? 0 : typeFace.getStyle()});
            }
            //Will catch the view with no such methods (listview...)
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Этот метод возвращает стиль представления, заданный в XML (полужирный, курсив ...), и применяет их, если они существуют.

Для ListView я всегда создаю адаптер и устанавливаю шрифт внутри getView.


2

Я написал класс, назначающий гарнитуру для представлений в текущей иерархии представлений и основываясь на текущих свойствах гарнитуры (жирный, обычный, вы можете добавить другие стили, если хотите):

public final class TypefaceAssigner {

public final Typeface DEFAULT;
public final Typeface DEFAULT_BOLD;

@Inject
public TypefaceAssigner(AssetManager assetManager) {
    DEFAULT = Typeface.createFromAsset(assetManager, "TradeGothicLTCom.ttf");
    DEFAULT_BOLD = Typeface.createFromAsset(assetManager, "TradeGothicLTCom-Bd2.ttf");
}

public void assignTypeface(View v) {
    if (v instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) v).getChildCount(); i++) {
            View view = ((ViewGroup) v).getChildAt(i);
            if (view instanceof ViewGroup) {
                setTypeface(view);
            } else {
                setTypeface(view);
            }
        }
    } else {
        setTypeface(v);
    }
}

private void setTypeface(View view) {
    if (view instanceof TextView) {
        TextView textView = (TextView) view;
        Typeface typeface = textView.getTypeface();
        if (typeface != null && typeface.isBold()) {
            textView.setTypeface(DEFAULT_BOLD);
        } else {
            textView.setTypeface(DEFAULT);
        }
    }
}
}

Теперь во всех фрагментах в onViewCreated или onCreateView, во всех действиях в onCreate и во всех адаптерах представления в getView или newView просто вызовите:

typefaceAssigner.assignTypeface(view);

2

в API 26 с build.gradle 3.0.0 и выше вы можете создать каталог шрифтов в Res и использовать эту строку в своем стиле

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

для изменения build.gradle используйте это в ваших зависимостях build.gradle

classpath 'com.android.tools.build:gradle:3.0.0'

а как вставить шрифт в проект?
Азерафати

@azerafati скопируйте ваш шрифт в res / font
Мохаммед Хосейн Хейдари

да, танкс Я уже получил это. Но itemодно это не устанавливает шрифт для всего приложения. какая-нибудь подсказка?
Азерафати

1

Наконец, Google осознал серьезность этой проблемы (применяя пользовательский шрифт к компонентам пользовательского интерфейса), и они разработали чистое решение для этой проблемы.

Сначала вам нужно выполнить обновление для поддержки библиотеки 26+ (вам также может понадобиться обновить ваш gradle {4.0+}, android studio), затем вы можете создать новую папку ресурсов с именем font. В эту папку вы можете поместить ресурсы шрифта (.tff, ...). Затем вам нужно переопределить приложение по умолчанию их и заставить свой собственный шрифт в него :)

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

Примечание: если вы хотите поддерживать устройства с более старым API, чем 16, вы должны использовать пространство имен приложения вместо Android!


0

Я также хотел бы улучшить ответ Вестона для API 21 Android 5.0.

У меня была такая же проблема на моем Samsung s5, когда я использовал шрифт DEFAULT. (с другими шрифтами он работает нормально)

Мне удалось заставить его работать, установив гарнитуру (например, "sans") в файлах XML для каждого Textview или кнопки

<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:textColor="@color/abs__background_holo_light"
android:textSize="12sp"
android:gravity="bottom|center"
android:typeface="sans" />

и в классе MyApplication:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
    TypefaceUtil.overrideFont(getApplicationContext(), "SANS_SERIF",
    "fonts/my_font.ttf");
    }
}

Надеюсь, поможет.



0

каллиграфия работает довольно хорошо, но она мне не подходит, поскольку она не поддерживает разные веса (полужирный, курсив и т. Д.) Для семейства шрифтов.

Поэтому я попробовал Fontain , который позволяет вам определять собственные представления и применять их к семействам пользовательских шрифтов.

чтобы использовать Fontain, вы должны добавить в свой модуль приложения build.gradle следующее:

compile 'com.scopely:fontain:1.0.0'

Затем вместо обычного TextView следует использовать FontTextView.

Пример FontTextView с заглавными и жирными буквами:

 <com.scopely.fontain.views.FontTextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/black"
            android:textColor="@android:color/white"
            android:textSize="11dp"
            android:gravity="center"
            android:id="@+id/tv1"
            app:font_family="myCustomFont"
            app:caps_mode="characters"
            app:font_weight="BOLD"/>

0
package com.theeasylearn.demo.designdemo;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;

public class MyButton extends TextView {

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

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

    public MyButton(Context context) {
        super(context);
        init();
    }

    private void init() {

            Typeface tf =
                    Typeface.createFromAsset(
                            getContext().getAssets(), "angelina.TTF");
            setTypeface(tf);

    }

}

Не идеальное решение. Используя это решение потребует пользовательских обходные пути для доступа по умолчанию Android TextViews
BlueWare

0

Чтобы изменить семейство шрифтов по умолчанию для TextViews, переопределите textViewStyle в теме вашего приложения.

Для использования собственного шрифта в fontFamily используйте ресурсы шрифтов, которые есть в библиотеке поддержки.

Функция была добавлена ​​в Android 26, но перенесена в более старые версии через supportlib.

https://developer.android.com/guide/topics/resources/font-resource.html https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html # с использованием-поддержка Пб


0

С момента выпуска Android Oreo и его библиотеки поддержки (26.0.0) вы можете легко это сделать. Обратитесь к этому ответу в другом вопросе.

В основном ваш окончательный стиль будет выглядеть так:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
   <item name="fontFamily">@font/your_font</item> <!-- target android sdk versions < 26 and > 14 -->
</style>


-15

Да, можно установить шрифт для всего приложения.

Самый простой способ сделать это - упаковать нужный шрифт (ы) в ваше приложение.

Для этого просто создайте активы / папку в корне проекта и поместите ваши шрифты (в форме TrueType или TTF) в ресурсы.

Вы можете, например, создать assets / fonts / и поместить туда свои файлы TTF.

public class FontSampler extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView tv=(TextView)findViewById(R.id.custom);

Typeface face=Typeface.createFromAsset(getAssets(), "fonts/HandmadeTypewriter.ttf");
tv.setTypeface(face);
}
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.