Використання власного шрифту в android TextView за допомогою xml


92

Я додав спеціальний файл шрифту до папки "Мої активи / шрифти". Як я можу використовувати його з мого XML?

Я можу використовувати його з коду наступним чином:

TextView text = (TextView) findViewById(R.id.textview03);
Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Molot.otf");
text.setTypeface(tf);

Чи не можу я це зробити з XML за допомогою android:typeface="/fonts/Molot.otf"атрибута?


Я багато шукав це, і ніяк не можна зробити це з xml.
Cd

2
спробуйте перевірити цей пост stackoverflow.com/questions/2376250 / ...
dor506

Погляньте на цю відповідь ! Це дозволяє використовувати кілька шрифтів та використовувати XML.
Rafa0809

Як сказано нижче, для досягнення цього можна використовувати каліграфію .
mbonnin

Перегляньте цю статтю gadgetsaint.com/tips/…
ASP

Відповіді:


45

Коротка відповідь: Ні. Android не має вбудованої підтримки застосування спеціальних шрифтів до текстових віджетів за допомогою XML.

Однак є обхідний шлях, який не надзвичайно складно здійснити.

Перший

Вам потрібно буде визначити свій власний стиль. У вашій папці / res / values ​​відкрийте / створіть файл attrs.xml і додайте об'єкт, який можна оголосити в стилі приблизно так:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FontText">
        <attr name="typefaceAsset" format="string"/>
    </declare-styleable>
</resources>

По-друге

Припускаючи, що ви хочете часто використовувати цей віджет, вам слід встановити простий кеш для завантажених Typefaceоб'єктів, оскільки завантаження їх з пам'яті на льоту може зайняти час. Щось на зразок:

public class FontManager {
    private static FontManager instance;

    private AssetManager mgr;

    private Map<String, Typeface> fonts;

    private FontManager(AssetManager _mgr) {
        mgr = _mgr;
        fonts = new HashMap<String, Typeface>();
    }

    public static void init(AssetManager mgr) {
        instance = new FontManager(mgr);
    }

    public static FontManager getInstance() {
        if (instance == null) {
            // App.getContext() is just one way to get a Context here
            // getContext() is just a method in an Application subclass
            // that returns the application context
            AssetManager assetManager = App.getContext().getAssets();
            init(assetManager);
        }
        return instance;
    }

    public Typeface getFont(String asset) {
        if (fonts.containsKey(asset))
            return fonts.get(asset);

        Typeface font = null;

        try {
            font = Typeface.createFromAsset(mgr, asset);
            fonts.put(asset, font);
        } catch (Exception e) {

        }

        if (font == null) {
            try {
                String fixedAsset = fixAssetFilename(asset);
                font = Typeface.createFromAsset(mgr, fixedAsset);
                fonts.put(asset, font);
                fonts.put(fixedAsset, font);
            } catch (Exception e) {

            }
        }

        return font;
    }

    private String fixAssetFilename(String asset) {
        // Empty font filename?
        // Just return it. We can't help.
        if (TextUtils.isEmpty(asset))
            return asset;

        // Make sure that the font ends in '.ttf' or '.ttc'
        if ((!asset.endsWith(".ttf")) && (!asset.endsWith(".ttc")))
            asset = String.format("%s.ttf", asset);

        return asset;
    }
}

Це дозволить вам використовувати розширення .ttc, але воно не перевірено.

По-третє

Створіть новий клас, який є підкласами TextView. Цей конкретний приклад враховує визначений шрифт XML ( bold, italicтощо) та застосовує його до шрифту (за умови, що ви використовуєте файл .ttc).

/**
 * TextView subclass which allows the user to define a truetype font file to use as the view's typeface.
 */
public class FontText extends TextView {
    public FontText(Context context) {
        this(context, null);
    }

    public FontText(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FontText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        if (isInEditMode())
            return;

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FontText);

        if (ta != null) {
            String fontAsset = ta.getString(R.styleable.FontText_typefaceAsset);

            if (!TextUtils.isEmpty(fontAsset)) {
                Typeface tf = FontManager.getInstance().getFont(fontAsset);
                int style = Typeface.NORMAL;
                float size = getTextSize();

                if (getTypeface() != null)
                    style = getTypeface().getStyle();

                if (tf != null)
                    setTypeface(tf, style);
                else
                    Log.d("FontText", String.format("Could not create a font from asset: %s", fontAsset));
            }
        }
    }
}

Нарешті

Замініть екземпляри TextViewу своєму XML повноцінним іменем класу. Заявіть свій власний простір імен так само, як і простір імен Android. Зверніть увагу, що "typefaceAsset" повинен вказувати на файл .ttf або .ttc, що міститься у вашому каталозі / assets.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.FontText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is a custom font text"
        custom:typefaceAsset="fonts/AvenirNext-Regular.ttf"/>
</RelativeLayout>

Чудова відповідь! Хоча одне питання: чому ви повернулися, коли isInEditMode?
Аві Шукрон

04-11 18: 18: 32.685 3506-3506 / com.example.demo E / AndroidRuntime ﹕ FATAL EXCEPTION: основний процес: com.example.demo, PID: 3506 android.view.InflateException: Бінарний рядок XML-файлу №2: Помилка роздування класу com.example.demo.libraries.LatoTextView на android.view.LayoutInflater.createView (LayoutInflater.java:620) на android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:696) на android.viewter.Lay LayoutInflater.java:469) на android.view.LayoutInflater.inflate (LayoutInflater.java:397) на ...
e-info128,

@AvrahamShukron - Це все тому, що я постійно отримував помилку в Android Studio, перебуваючи в режимі попереднього перегляду (мабуть, тому, що не знає, як обробляти спеціальний шрифт.
themarshal

Додано xmlns: custom = " schemas.android.com/tools " у RelativeLayout, і помилка знищена . Спасибі
Навід Ахмад

1
Привіт @themarshal: замість xmlns: custom = " schemas.android.com/tools " вам слід використовувати: xmlns: custom = " schemas.android.com/apk/res-auto ", щоб використовувати стильові атрибути.
Абхінав Саксена

28

Ось приклад коду, який це робить. Я маю шрифт, визначений у статичній кінцевій змінній, і файл шрифту знаходиться в каталозі активів.

public class TextViewWithFont extends TextView {

    public TextViewWithFont(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setTypeface(MainActivity.typeface);
    }

    public TextViewWithFont(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setTypeface(MainActivity.typeface);
    }

    public TextViewWithFont(Context context) {
        super(context);
        this.setTypeface(MainActivity.typeface);
    }

}

5
OP спеціально зазначає, що він знає, як програмувати шрифт (він навіть наводить приклад). Питання в тому, як встановити шрифт у xml?
SMBiggs

13

Створіть свій власний TextView, який належить до шрифту, який ви хочете використовувати. У цьому класі я використовую статичне поле mTypeface для кешування Typeface (для кращої продуктивності)

public class HeliVnTextView extends TextView {

/*
 * Caches typefaces based on their file path and name, so that they don't have to be created every time when they are referenced.
 */
private static Typeface mTypeface;

public HeliVnTextView(final Context context) {
    this(context, null);
}

public HeliVnTextView(final Context context, final AttributeSet attrs) {
    this(context, attrs, 0);
}

public HeliVnTextView(final Context context, final AttributeSet attrs, final int defStyle) {
    super(context, attrs, defStyle);

     if (mTypeface == null) {
         mTypeface = Typeface.createFromAsset(context.getAssets(), "HelveticaiDesignVnLt.ttf");
     }
     setTypeface(mTypeface);
}

}

У файлі xml:

<java.example.HeliVnTextView
        android:id="@+id/textView1"
        android:layout_width="0dp"
        ... />

У класі Java:

HeliVnTextView title = new HeliVnTextView(getActivity());
title.setText(issue.getName());

11

Activity реалізує LayoutInflater.Factory2, який забезпечує зворотні виклики для кожного створеного подання. Можна стилізувати TextView за допомогою власного атрибута Family font, завантажувати шрифти на вимогу та автоматично викликати setTypeface у екземплярах текстових подань.

На жаль, завдяки архітектурному взаємозв'язку примірників Inflater щодо діяльності та Windows, найпростіший підхід до використання нестандартних шрифтів в Android - це кешування завантажених шрифтів на рівні програми.

Зразок бази коду знаходиться тут:

https://github.com/leok7v/android-textview-custom-fonts

  <style name="Baroque" parent="@android:style/TextAppearance.Medium">
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textColor">#F2BAD0</item>
    <item name="android:textSize">14pt</item>
    <item name="fontFamily">baroque_script</item>
  </style>

  <?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/custom.fonts"
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
  >
  <TextView
    style="@style/Baroque"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/sample_text"
  />

призводить до

введіть тут опис зображення


Не чіпав цей код протягом 2 років. Але це повинно бути. Я не бачу нічого в теорії, що це заважає.
Лев

7

Не хороша ідея використовувати власні шрифти в xml, тому що це потрібно робити програмно, щоб уникнути витоку пам’яті!


6
Здається, витік пам’яті було виправлено в Ice Cream Sandwich.
Michael Scheper

2
Вам слід використовувати метод TypedArray recycle (), щоб уникнути витоків пам'яті.
Абхінав Саксена

7

ОНОВЛЕННЯ: https://github.com/chrisjenx/ Каліграфія видається найкращим рішенням для цього.


Можливо, ви можете використовувати відображення, щоб вставити / зламати ваш шрифт у статичний список доступних шрифтів при створенні вашої програми? Мене цікавлять відгуки інших, якщо це дійсно, дуже погана ідея або це чудове рішення - здається, це буде одна з цих крайнощів ...

Я зміг внести свій власний шрифт у список системних шрифтів із власним прізвищем шрифту, а потім вказавши це власне прізвище шрифту («кисть-скрипт») як значення android: FontFamily на стандартному TextView, що працював на моєму LG G4 під управлінням Android 6.0.

public class MyApplication extends android.app.Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();

        Typeface font = Typeface.createFromAsset(this.getResources().getAssets(),"fonts/brush-script.ttf");
        injectTypeface("brush-script", font);
    }

    private boolean injectTypeface(String fontFamily, Typeface typeface)
    {
        try
        {
            Field field = Typeface.class.getDeclaredField("sSystemFontMap");
            field.setAccessible(true);
            Object fieldValue = field.get(null);
            Map<String, Typeface> map = (Map<String, Typeface>) fieldValue;
            map.put(fontFamily, typeface);
            return true;
        }
        catch (Exception e)
        {
            Log.e("Font-Injection", "Failed to inject typeface.", e);
        }
        return false;
    }
}

У моєму макеті

<TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Fancy Text"
    android:fontFamily="brush-script"/>

Мені це не підходить, чи можете ви розповісти детальніше, як ним користуватися? Чи MyApplicationпотрібно викликати клас?
Дадан

@Yawz, вам потрібно буде викликати метод injectTypeface принаймні один раз у вашому додатку. Якщо він реєструє виняток, мене зацікавлять деталі. Я протестував це лише на своєму LG G4 з операційною системою Android 6.0 (на той час я проводив деякі власні дослідження), і я сподіваюся, що це працює не у всіх версіях Android.
Росс Бредбері

@RossBradbury це не працює на деяких пристроях .. більшість пристроїв працюють коректно, але деякі пристрої не приймають цей шрифт .. мій випробуваний пристрій - модель lenova a7000
Ранджит Кумар

ОНОВЛЕННЯ: github.com/chrisjenx/Calligraphy - чудове рішення.
Росс Бредбері

4

Створіть папку шрифтів в ресурсах та додайте туди всі потрібні шрифти.

public class CustomTextView extends TextView {
    private static final String TAG = "TextView";

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

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setCustomFont(context, attrs);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setCustomFont(context, attrs);
    }

    private void setCustomFont(Context ctx, AttributeSet attrs) {
        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
        String customFont = a.getString(R.styleable.CustomTextView_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    }

    public boolean setCustomFont(Context ctx, String fontName) {
        Typeface typeface = null;
        try {
            if(fontName == null){
                fontName = Constants.DEFAULT_FONT_NAME;
            }
            typeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/" + fontName);
        } catch (Exception e) {
            Log.e(TAG, "Unable to load typeface: "+e.getMessage());
            return false;
        }
        setTypeface(typeface);
        return true;
    }
}

та додайте декларований у attrs.xml

<declare-styleable name="CustomTextView">
      <attr name="customFont" format="string"/>
</declare-styleable>

а потім додайте свій customFont like

app:customFont="arial.ttf"

Ви також можете налаштувати вищезазначений клас для роботи зі стилем, як це зроблено тут. github.com/leok7v/android-textview-custom-fonts/blob/master/res/…
AndroidGeek

4

Я знаю, що це старе запитання, але я знайшов набагато простіше рішення.

Спочатку оголосіть свій TextView у форматі xml, як зазвичай. Помістіть шрифт (TTF або TTC) у папку об’єкта

app \ src \ main \ assets \

Потім просто встановіть шрифт для подання тексту в методі onCreate.

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

    TextView textView = findViewById(R.id.my_textView);
    Typeface typeface = Typeface.createFromAsset(getAssets(), "fontName.ttf");
    textView.setTypeface(typeface);
}

Готово.


1
Питання чітко говорить про те, щоб використовувати його у файлах xml
Густаво Байоккі Коста,


0

замість xmlns: custom = "schemas.android.com/tools"; слід використовувати: xmlns: custom = "schemas.android.com/apk/res-auto"; для того, щоб використовувати стильові атрибути. Я вніс цю зміну, і вона працює зараз.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.