Чи можна встановити спеціальний шрифт для всієї програми?


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 ласка , прочитайте DOC developer.android.com/guide/topics/ui/look-and-feel / ... . ця функція додана в бібліотеці підтримки 26, але підтримка API 16.
MHSFisher

Відповіді:


449

Так, з роздумом. Це працює ( виходячи з цієї відповіді ):

(Примітка. Це рішення вирішено через відсутність підтримки користувацьких шрифтів, тому, якщо ви хочете змінити цю ситуацію, будь ласка, позначте тут питання про 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]на вашTypeface. Дивіться це.
weston

5
@weston Неважливо. Я цього досяг. У моєму розумінні сталася помилка. Ви зробили чудову роботу. Ви можете просто сказати мені одне? Чому він не працює для DEFAULT Typeface? Я змінив Typeface на MONOSPACE як вашу пропозицію, а потім застосував loginc, він спрацював. Але не працює для DEFAULT
Jay Pandya

2
Рідний шрифт не може бути помилкою. Виправлено, додавши шлях шрифтів. "FontsOverride.setDefaultFont (це" DEFAULT "," шрифти / MyFontAsset.ttf ");"
Sai

3
Мені здається, що це насправді дратує, що потрібно міняти місцями за замовчуванням TextViewскрізь, щоб використовувати власні шрифти або використовувати рішення, що базуються на відображенні. У поєднанні з обмежувальним набором шрифтів, доступних в андроїді, це робить розробку гарних прикладних програм дуже важкою роботою. Я додав запит на функцію до трекера проблем Android. Якщо ви хотіли б бачити цю функцію, ви можете зірка питання тут: code.google.com/p/android/issues / ...
Sam

64

В андроїді є велика бібліотека для користувацьких шрифтів: 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">

і це змінить всю діяльність на ваш шрифт! це просто і чисто!


1
Це ідеальне рішення.
nizam.sp

1
Правда, його дуже ідеальне рішення, тому мені не доведеться писати різні класи для оновлення шрифтів для пунктів меню, радіо-кнопки або фотки прапорців. Це стосується всіх !! дякую :)
Manisha

2
Однозначно не ідеальне рішення: Жирний / курсивний / жирний курсив стилі переписані. Наразі не існує простого способу встановити ці сім'ї.
0101100101

2
Якщо я це роблю Як я можу перейти від жирного до звичайного?
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 Marker тощо все одно використовуватиме шрифт системи.
csch

2
хлопці, ця функція приймає 3 параметри, коли ви викликаєте її рекурсивно, ви передали 2 параметри ??? який правильний? що відображає ??
MBH

34

Підсумовуючи:

Варіант №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();
            } 
        }
    }

} 

Використання в класі застосування:

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

Попередній льодяник:

<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, кнопка тощо (відповідь Палані ):

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 (відповідь Тома ):

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: Використовуйте сторонню губу під назвою Каліграфія .

Особисто я б рекомендував Варіант №4, оскільки це економить багато головних болів.


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

у варіанті 1 перший фрагмент коду не працює з рівнем API 22, Android 5.1.1. Але інший розділ коду є. Що має бути точним, якщо стан так?
користувач1510006

28

Я хотів би покращити відповідь Вестона щодо API 21 Android 5.0.

Причина

Під API 21 більшість стилів тексту включає налаштування шрифтуFamily, наприклад:

<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 ( посилання ), ми можемо використовувати ту саму техніку відображення, щоб замінити його:

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назв s та Tool Tool. Я перевизначення шрифта в 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+. Не перекривайте шрифт у ваших стилях value-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/isissue/detail?id=9904
Ryan M

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

8

Я хотів би покращити відповіді Уестона та Роджера Хуанга для льодяника API для 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 такий же, як і у Вестоні відповідь «s. Я пройшов тестування на таких телефонах:

Nexus 5 (Android Android 5.1)

ZTE V5 (android 5.1 CM12.1)

Примітка XIAOMI (android 4.4 MIUI6)

HUAWEI C8850 (андроїд 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, іноді - null. Навіть якщо 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, щоб мати змогу додавати папку шрифтів.
Photon Point

насправді мені просто потрібно застосувати його до свого стилю, і це буде застосовано через додаток. але деякі (програмно додані елементи управління повинні бути застосовані через java-код)
SolidSnake

4

Ви можете встановити спеціальні шрифти для кожного макета по одному, за допомогою лише одного виклику функції з кожного макета, передаючи його кореневий вигляд. Спочатку створіть підхід singelton для доступу до такого об’єкта шрифту, як цей

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

У верхньому макеті основний ідентифікатор root - "Main Parent", тепер дозволяє застосовувати шрифт

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

Тепер у всіх фрагментах в onViewCreate або 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

@azerafati скопіюйте свій шрифт у res / font
Mohammad Hosein Heidari

так, tanx. Я вже це зрозумів. Але це itemлише не встановлює шрифт для всього додатка. якась підказка?
azerafati

1

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

По-перше, вам потрібно оновити для підтримки бібліотеку 26+ (можливо, вам також знадобиться оновити свій gradle {4.0+}, android studio), потім ви можете створити нову папку ресурсів під назвою шрифт. У цій папці ви можете помістити ресурси шрифту (.tff, ...). Тоді вам потрібно змінити додаток за замовчуванням на них і примусити до нього свій власний шрифт :)

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

Примітка: якщо ви хочете підтримувати пристрої зі старшим API, ніж 16, вам потрібно використовувати простір імен додатків замість android!


0

Я також хотів би покращити відповідь Weston для 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 # using-support-lib


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) до активів.

Наприклад, ви можете створити активи / шрифти / і помістити туди свої файли 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.