Использование пользовательского шрифта в android TextView с помощью xml

Я добавил файл пользовательских шрифтов в папку my assets/fonts. Как использовать его из моего 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"?

Ответ 1

Короткий ответ: Нет. 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 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>

Ответ 2

Вот пример кода, который делает это. У меня есть шрифт, определенный в статической конечной переменной, а файл шрифта находится в каталоге ресурсов.

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

}

Ответ 3

Действие реализует LayoutInflater.Factory2, который обеспечивает обратные вызовы для каждого созданного Просмотра. Можно стилизовать TextView с помощью специального атрибута fontFamily, загружать шрифты по требованию и автоматически вызывать setTypeface при создании экземпляров автоматически.

К сожалению, из-за архитектурных отношений экземпляров Inflater относительно действий и Windows самый простой подход заключается в кэшировании загруженных шрифтов на уровне приложения.

База кода примера находится здесь:

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

приводит к

enter image description here

Ответ 4

Создайте свой собственный 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());

Ответ 5

Не рекомендуется использовать пользовательские шрифты в xml из-за этот факт то есть вы должны делать это программно, чтобы избежать утечки памяти!

Ответ 6

UPDATE: https://github.com/chrisjenx/Calligraphy кажется превосходное решение.


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

Мне удалось ввести свой собственный шрифт в список системных шрифтов с моим собственным именем семейства шрифтов, а затем указать, что собственное имя семейства шрифтов ( "brush- script" ) в качестве значения 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"/>

Ответ 7

Я знаю, что это старый вопрос, но я нашел гораздо более легкое решение.

Сначала объявите свой TextView в xml, как обычно. Поместите свой шрифт (TTF или TTC) в папку с ресурсами

Приложение\SRC\главная\активы\

Затем просто установите шрифт для вашего текстового вида в свой метод 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);
}

Готово.

Ответ 8

вместо xmlns: custom = "schemas.android.com/tools"; вы должны использовать: xmlns: custom = "schemas.android.com/apk/res-auto"; чтобы использовать атрибуты стиля. Я сделал это изменение, и он работает сейчас.

Ответ 9

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

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, например

app:customFont="arial.ttf"