Usando fonte personalizada no Android TextView usando xml

92

Eu adicionei um arquivo de fonte personalizada à minha pasta de recursos / fontes. Como faço para usá-lo no meu XML?

Posso usá-lo a partir do código da seguinte maneira:

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

Não posso fazer isso a partir de XML usando um android:typeface="/fonts/Molot.otf"atributo?

Nilanchal
fonte
Eu pesquisei muito isso e não há como você fazer isso a partir do xml.
Cd
2
tente conferir esta postagem stackoverflow.com/questions/2376250/…
dor506
Dê uma olhada nesta resposta ! Ele permite que você use fontes múltiplas e usando XML.
Rafa0809
Como outro dito abaixo, você pode usar caligrafia para fazer isso.
mbonnin
Verifique este artigo gadgetsaint.com/tips/…
ASP

Respostas:

45

Resposta curta: Não. O Android não tem suporte integrado para aplicar fontes personalizadas a widgets de texto por meio de XML.

No entanto, há uma solução alternativa que não é muito difícil de implementar.

Primeiro

Você precisará definir seu próprio estilo. Em sua pasta / res / values, abra / crie o arquivo attrs.xml e adicione um objeto declare estilizável como:

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

Segundo

Supondo que você queira usar esse widget com frequência, você deve configurar um cache simples para os Typefaceobjetos carregados , já que carregá-los da memória em tempo real pode levar algum tempo. Algo como:

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

Este permitirá que você use extensões de arquivo .ttc, mas não foi testado.

Terceiro

Crie uma nova classe com subclasses TextView. Este exemplo em particular leva em conta o tipo de letra XML definido ( bold, italic, etc.) e aplicá-lo à fonte (supondo que você está usando um arquivo .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));
            }
        }
    }
}

Finalmente

Substitua as instâncias de TextViewem seu XML pelo nome de classe totalmente qualificado. Declare seu namespace personalizado exatamente como faria com o namespace do Android. Observe que o "typefaceAsset" deve apontar para um arquivo .ttf ou .ttc contido em seu diretório / 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>
themarshal
fonte
Ótima resposta! Uma pergunta, porém: por que você retornou quando isInEditMode?
Avi Shukron
04-11 18: 18: 32.685 3506-3506 / com.example.demo E / AndroidRuntime ﹕ EXCEÇÃO FATAL: Processo principal: com.example.demo, PID: 3506 android.view.InflateException: Arquivo XML binário linha 2: Erro inflando a classe com.example.demo.libraries.LatoTextView em android.view.LayoutInflater.createView (LayoutInflater.java:620) em android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:696) em android.view.LayoutInflater.inflate ( LayoutInflater.java:469) em android.view.LayoutInflater.inflate (LayoutInflater.java:397) em ...
e-info128
@AvrahamShukron - Isso ocorre porque eu continuava recebendo um erro no Android Studio enquanto estava no modo de visualização (provavelmente porque ele não sabe como aplicar uma fonte personalizada.
themarshal
Adicionado xmlns: custom = " schemas.android.com/tools " em RelativeLayout e o erro desapareceu. Obrigado cara
Naveed Ahmad
1
Olá @themarshal: em vez de xmlns: custom = " schemas.android.com/tools ", você deve usar: xmlns: custom = " schemas.android.com/apk/res-auto " para usar atributos estilizáveis.
Abhinav Saxena
28

Aqui está um exemplo de código que faz isso. Eu tenho a fonte definida em uma variável final estática e o arquivo da fonte está no diretório de ativos.

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

}
Stephan Wiesner
fonte
5
O OP afirma especificamente que ele sabe como definir a fonte programaticamente (ele até fornece um exemplo). A questão é como definir a fonte em xml?
SMBiggs de
13

Crie seu TextView personalizado pertencente à fonte que você deseja usar. Nesta aula, uso um campo estático mTypeface para armazenar em cache o Typeface (para melhor desempenho)

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

}

No arquivo xml:

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

Na classe java:

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

Activity implementa LayoutInflater.Factory2 que fornece callbacks em cada View criado. É possível definir o estilo do TextView com o atributo Família de fonte personalizada, carregar os tipos de fonte sob demanda e chamar setTypeface em visualizações de texto instanciadas automaticamente.

Infelizmente, devido ao relacionamento arquitetônico das instâncias do Inflater relativas a Activities e Windows, a abordagem mais simples para usar fontes personalizadas no Android é armazenar em cache as fontes carregadas no nível do aplicativo.

O exemplo de base de código está aqui:

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

resulta em

insira a descrição da imagem aqui

Leo
fonte
Não toquei nesse código por 2 anos. Mas deveria. Não vejo nada em teoria impedindo isso.
Leo
7

Não é uma boa ideia usar fontes personalizadas em xml devido ao fato de que você tem que fazer isso programaticamente para evitar vazamento de memória!

type-a1pha
fonte
6
Parece que o vazamento de memória foi corrigido no Ice Cream Sandwich.
Michael Scheper
2
Você deve usar o método recycle () de TypedArray para evitar vazamentos de memória.
Abhinav Saxena
7

ATUALIZAÇÃO: https://github.com/chrisjenx/Calligraphy parece ser uma solução superior para isso.


Talvez você possa usar a reflexão para injetar / hackear sua fonte na lista estática de fontes disponíveis quando seu aplicativo é criado? Estou interessado no feedback de outras pessoas se isso for realmente uma ideia muito ruim ou se for uma ótima solução - parece que vai ser um daqueles extremos ...

Consegui injetar meu tipo de fonte personalizado na lista de fontes do sistema com meu próprio nome de família de fonte e, em seguida, especificando esse nome de família de fonte personalizada ("brush-script") como o valor de android: FontFamily em um TextView padrão funcionado em meu LG G4 com 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;
    }
}

No meu layout

<TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Fancy Text"
    android:fontFamily="brush-script"/>
Ross Bradbury
fonte
Não funciona para mim, você pode dar mais detalhes sobre como usá-lo? A MyApplicationclasse precisa ser chamada?
Dadan,
@Yawz, você precisaria chamar o método injectTypeface pelo menos uma vez em seu aplicativo. Se ele registrar uma exceção, estou interessado nos detalhes. Eu só testei isso no meu LG G4 com Android 6.0 (eu estava fazendo minha própria pesquisa na época) e espero que não funcione em todas as versões do Android.
Ross Bradbury
@RossBradbury não funciona em alguns dispositivos .. a maioria dos dispositivos funcionando corretamente, mas alguns dispositivos não aceitam esta fonte família .. meu dispositivo testado - modelo lenova a7000
Ranjith Kumar
ATUALIZAÇÃO: github.com/chrisjenx/Calligraphy é uma solução superior.
Ross Bradbury
4

Crie uma pasta de fontes em recursos e adicione todas as fontes necessárias lá.

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

e adicione um declarável em attrs.xml

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

e, em seguida, adicione seu customFont like

app:customFont="arial.ttf"
AndroidGeek
fonte
você também pode personalizar acima da classe para trabalhar com estilo, como feito aqui. github.com/leok7v/android-textview-custom-fonts/blob/master/res/…
AndroidGeek
4

Sei que essa é uma pergunta antiga, mas encontrei uma solução muito mais fácil.

Primeiro declare seu TextView em xml como de costume. Coloque sua fonte (TTF ou TTC) na pasta de ativos

app \ src \ main \ assets \

Em seguida, basta definir o tipo de letra para sua visualização de texto em seu método 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);
}

Feito.

Oiproks
fonte
1
A questão afirma claramente para usá-lo dentro de arquivos xml não programaticamente
Gustavo Baiocchi Costa
0

em vez de xmlns: custom = "schemas.android.com/tools"; você deve usar: xmlns: custom = "schemas.android.com/apk/res-auto"; para usar atributos estilizáveis. Fiz essa alteração e ela está funcionando agora.

Abhinav Saxena
fonte