Fontes personalizadas e layouts XML (Android)


174

Estou tentando definir um layout de GUI usando arquivos XML no Android. Pelo que pude descobrir, não há como especificar que seus widgets devem usar uma fonte personalizada (por exemplo, uma que você colocou em assets / font /) em arquivos XML e você só pode usar as fontes instaladas no sistema.

Eu sei que, no código Java, eu poderia alterar a fonte de cada widget manualmente usando IDs exclusivos. Como alternativa, eu poderia percorrer todos os widgets em Java para fazer essa alteração, mas isso provavelmente seria muito lento.

Que outras opções eu tenho? Existe alguma maneira melhor de criar widgets com uma aparência personalizada? Eu particularmente não quero ter que alterar manualmente a fonte para cada novo widget adicionado.


68
DrDefrost - aceite algumas respostas, você obterá mais respostas neste site.
SK9

1
Aqui está mais uma pergunta semelhante: stackoverflow.com/questions/9030204/…
Vins

Atualizado em 05/2017: "A Biblioteca de suporte 26.0 Beta fornece suporte ao recurso Fontes em XML em dispositivos executando a API Android 14 e superior." Veja: developer.android.com/preview/features/…
albert c braun

Respostas:


220

Você pode estender o TextView para definir fontes personalizadas, como aprendi aqui .

TextViewPlus.java:

package com.example;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

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

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

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

    public TextViewPlus(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.TextViewPlus);
        String customFont = a.getString(R.styleable.TextViewPlus_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    }

    public boolean setCustomFont(Context ctx, String asset) {
        Typeface tf = null;
        try {
        tf = Typeface.createFromAsset(ctx.getAssets(), asset);  
        } catch (Exception e) {
            Log.e(TAG, "Could not get typeface: "+e.getMessage());
            return false;
        }

        setTypeface(tf);  
        return true;
    }

}

attrs.xml: (em res / valores)

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

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:foo="http://schemas.android.com/apk/res/com.example"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <com.example.TextViewPlus
        android:id="@+id/textViewPlus1"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:text="@string/showingOffTheNewTypeface"
        foo:customFont="saxmono.ttf">
    </com.example.TextViewPlus>
</LinearLayout>

Você colocaria "saxmono.ttf" na pasta de ativos .

ATUALIZAÇÃO 1/8/13

Há sérias preocupações de memória com esse método. Veja o comentário de chedabob abaixo.


5
Parece bom, no entanto, estou recebendo um erro ao tentar usar o "TextViewPlus" no main.xml. Recebo o seguinte: - erro: Erro ao analisar XML: prefixo não acoplado - O prefixo "foo" para o atributo "foo: customFont" associado a um tipo de elemento "supportLibs.TextViewPlus" não está vinculado.
Majjoodi

79
Uma coisa a notar é que isso irá gerar dezenas e dezenas de objetos TypeFace e consumir memória. Há um bug no Android anterior à 4.0 que não libera o TypeFaces corretamente. A coisa mais fácil a fazer é criar um cache TypeFace com um HashMap. Isso reduziu o uso de memória no meu aplicativo de 120 MB para 18 MB. code.google.com/p/android/issues/detail?id=9904
chedabob

7
@Majjoodi: Isso normalmente acontece se você esquecer de adicionar o segundo espaço para nome ao seu layout:xmlns:foo="http://schemas.android.com/apk/res/com.example
Theo

11
MUITO IMPORTANTE - Gostaria apenas de dobrar o conselho de Chedabob. A menos que você o siga, haverá um vazamento de memória no pré-ICS. Existem poucas soluções, uma delas está no link fornecido pelo chedabob, outra aqui: stackoverflow.com/questions/8057010/listview-memory-leak . peter - por favor atualize sua resposta - é ótimo, mas não completa
Michał K

1
Como defino esse atributo personalizado em styles.xml, além de outros atributos da visualização de texto, como largura e altura?
21812 loeschg #

35

Estou 3 anos atrasado para a festa :( No entanto, isso pode ser útil para alguém que possa encontrar este post.

Eu escrevi uma biblioteca que armazena em cache tipos de letra e também permite que você especifique tipos de letra personalizados diretamente do XML. Você pode encontrar a biblioteca aqui .

Aqui está a aparência do seu layout XML quando você o usar.

<com.mobsandgeeks.ui.TypefaceTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/hello_world"
    geekui:customTypeface="fonts/custom_font.ttf" />

Hey @ Ragunath-jawahar, como eu importaria a biblioteca para um projeto gradle? Eu tentei, compile 'com.mobsandgeeks:android-typeface-textview:1.0'mas isso não funcionou.
Aimango

1
Você precisará de um AAR para poder usá-lo dessa maneira. Ainda não está lá. Você pode copiar as fontes e criar um projeto da Biblioteca do Android Studio por enquanto.
Ragunath Jawahar

2
de onde vem a sua tag geekui?
sefirosu

3
A partir do namespace especificado na tag pai -xmlns:geekui="http://schemas.android.com/apk/res-auto"
Ragunath Jawahar

1
@MuhammedRefaat, sim ele faz :)
Ragunath Jawahar

18

Pode ser um pouco tarde, mas você precisa criar uma classe singleton que retorne o tipo de letra personalizado para evitar vazamentos de memória.

Classe TypeFace:

public class OpenSans {

private static OpenSans instance;
private static Typeface typeface;

public static OpenSans getInstance(Context context) {
    synchronized (OpenSans.class) {
        if (instance == null) {
            instance = new OpenSans();
            typeface = Typeface.createFromAsset(context.getResources().getAssets(), "open_sans.ttf");
        }
        return instance;
    }
}

public Typeface getTypeFace() {
    return typeface;
}
}

TextView personalizado:

public class NativelyCustomTextView extends TextView {

    public NativelyCustomTextView(Context context) {
        super(context);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

    public NativelyCustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

    public NativelyCustomTextView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
        setTypeface(OpenSans.getInstance(context).getTypeFace());
    }

}

Por xml:

<com.yourpackage.views.NativelyCustomTextView
            android:id="@+id/natively_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="20dp"
            android:text="@string/natively"
            android:textSize="30sp" /> 

Programaticamente:

TextView programmaticallyTextView = (TextView) 
       findViewById(R.id.programmatically_text_view);

programmaticallyTextView.setTypeface(OpenSans.getInstance(this)
                .getTypeFace());

Parece funcionar em tempo de execução, mas também deve funcionar no designer?
Magnus Johansson

Certo. Você pode usar como xml. @Magnus
Leonardo Cardoso

Eu acho que você entendeu errado, parece não funcionar no tempo de design (visualização do Designer). (xml! = designer). Ele funciona bem especificado no arquivo de layout Xml compilado em tempo de execução. De qualquer forma, estou usando esta extensão github.com/danh32/Fontify , que funciona muito melhor para minhas necessidades (suporta vários estilos de fonte, regulares, negritos etc., além de nomes de fontes diferentes e outros controles além do TextView)
Magnus Johansson

2
O getTypeFace cria um novo tipo de letra a cada chamada ... isso derrota o objetivo do singleton. Ele deve ter um campo estático definido na primeira vez que a chamada é feita e retorna o valor do campo nas chamadas subseqüentes.
Steven Pena

1
@chiwai você está certo! Sobre a primeira pergunta, ela não precisa. Sobre o segundo, foi um erro de digitação.
Leonardo Cardoso

13

Pergunta antiga, mas com certeza gostaria de ler esta resposta aqui antes de iniciar minha própria busca por uma boa solução. A caligrafia estende o android:fontFamilyatributo para adicionar suporte a fontes personalizadas na sua pasta de ativos, da seguinte forma:

<TextView 
  android:text="@string/hello_world"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:fontFamily="fonts/Roboto-Bold.ttf"/>

A única coisa que você precisa fazer para ativá-lo é anexá-lo ao contexto da atividade que você está usando:

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

Você também pode especificar seu próprio atributo personalizado para substituir android:fontFamily

Também funciona em temas, incluindo o AppTheme.


8

Usando DataBinding :

@BindingAdapter({"bind:font"})
public static void setFont(TextView textView, String fontName){
 textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));
}

Em XML:

<TextView
app:font="@{`Source-Sans-Pro-Regular.ttf`}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

arquivo de fonte deve estar em assets/fonts/


7

Se você tiver apenas um tipo de letra que gostaria de adicionar e desejar menos código para escrever, poderá criar um TextView dedicado para sua fonte específica. Veja o código abaixo.

package com.yourpackage;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;

public class FontTextView extends TextView {
    public static Typeface FONT_NAME;


    public FontTextView(Context context) {
        super(context);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
    public FontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
    public FontTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        if(FONT_NAME == null) FONT_NAME = Typeface.createFromAsset(context.getAssets(), "fonts/FontName.otf");
        this.setTypeface(FONT_NAME);
    }
}

No main.xml, agora você pode adicionar seu textView assim:

<com.yourpackage.FontTextView
    android:id="@+id/tvTimer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="" />

faça isso em init () e economize 3x a mesma ligação.
ericosg

@ericosg eu recebo este erro quando eu uso yourthis solução android.view.InflateException: linha do arquivo XML binário # 9: Erro inflar classe com.ascent.adwad.utils.CustomTextView
Sagar Devanga

@SagarDevanga, difícil de ajudar sem mais informações. Talvez leve o mais longe que puder e faça uma nova pergunta.
ericosg

7

A melhor maneira de fazer isso Na versão de pré-visualização do Android O, é assim:
1.) Clique com o botão direito do mouse na pasta res e vá para Novo> diretório de recursos do Android . A
janela Novo Diretório de Recursos é exibida.
2.) Na lista Tipo de recurso, selecione a fonte e clique em OK.
3.) Adicione seus arquivos de fonte na pasta de fontes . A estrutura de pastas abaixo gera R.font.dancing_script, R.font.la_la e R.font.ba_ba.
4.) Clique duas vezes em um arquivo de fonte para visualizar as fontes do arquivo no editor.

Em seguida, devemos criar uma família de fontes

1.) Clique com o botão direito do mouse na pasta fonte e vá para Novo> Arquivo de Recurso Fonte . A janela Novo arquivo de recurso é exibida.
2.) Digite o nome do arquivo e clique em OK . O novo XML do recurso de fonte é aberto no editor.
3.) Coloque cada atributo de arquivo, estilo e peso da fonte no elemento da tag da fonte. O XML a seguir ilustra como adicionar atributos relacionados à fonte no XML do recurso de fonte:

<?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/hey_regular" />
    <font
    android:fontStyle="italic"
    android:fontWeight="400"
    android:font="@font/hey_bababa" />
</font-family>

Adicionando fontes a um TextView:

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

A partir da documentação

Trabalhando com fontes

todas as etapas estão corretas.


1
A melhor resposta! O nome do arquivo da fonte deve estar em minúsculas.
Dmitry

Você também pode criar um estilo personalizado aplicado ao seu aplicativo (no arquivo AndroidManifest) e ele será dado a TODAS as visualizações. Em vez de, por exemplo, apenas colocá-lo em um único TextView, pois isso não afetará a barra de ferramentas.
goldenmaza

5

Estenda TextViewe atribua a ele um atributo personalizado ou apenas use o atributo android: tag para passar uma String de qual fonte você deseja usar. Você precisará escolher uma convenção e cumpri-la, pois eu colocarei todas as minhas fontes na pasta res / assets / fonts / para que sua classe TextView saiba onde encontrá-las. Então, no seu construtor, você apenas define a fonte manualmente após a super chamada.


4

A única maneira de usar fontes personalizadas é através do código fonte.

Lembre-se de que o Android roda em dispositivos com recursos e fontes muito limitados, podendo exigir uma boa quantidade de RAM. As fontes internas do Droid são feitas especialmente e, se você notar, faltam muitos caracteres e decorações.


"Lembre-se de que o Android roda em dispositivos com recursos muito limitados" -> isso está se tornando cada vez menos necessário. Telefone quad core? Realmente??
Sandy

9
Eu faria mais para mostrar a consideração do contexto da resposta do tareqHs, que antecede o seu comentário em uns bons 2 anos e meio.
SK9

A primeira parte da sua resposta pode ter sido verdadeira quando você escreveu sua resposta em 2010. A última é superfluoys e não vem ao caso: o Droid foi ruim, abandonou o Google em 2012 em favor do Roboto. A falta de escolha na tipografia no Android é uma falha, não um recurso; O iOS tem várias fontes disponíveis para os desenvolvedores desde 2008 em dispositivos primitivos para os padrões atuais.
Cosmix

Esta resposta não é mais válida.
Martin Konecny

2

Talvez eu tenha uma resposta simples para a pergunta sem estender o TextView e implementar um código longo.

Código:

 TextView tv = (TextView) findViewById(R.id.textview1);
    tv.setTypeface(Typeface.createFromAsset(getAssets(), "font.ttf"));

Coloque o arquivo de fonte personalizado na pasta de ativos, como de costume, e tente isso. Funciona para mim. Eu simplesmente não entendo por que Peter deu um código tão grande para essa coisa simples ou ele deu sua resposta na versão antiga.


2

Também pode ser definido no xml sem criar classes personalizadas

style.xml

<style name="ionicons" parent="android:TextAppearance">
    <!-- Custom Attr-->
    <item name="fontPath">fonts/ionicons.ttf</item>
</style>

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="@style/ionicons"
        android:text=""/>
</LinearLayout>

Uma observação rápida, porque eu sempre esqueci onde colocar as fontes, é que a fonte deve estar dentro assetse essa pasta reside no mesmo nível que rese src, no meu caso, éassets/fonts/ionicons.ttf

Atualizado o layout raiz adicionado, porque esse método precisa xmlns:app="http://schemas.android.com/apk/res-auto"funcionar

Atualização 2 Esqueci uma biblioteca que eu instalei antes chamada Calligraphy


Isso não funciona para mim. Quando tento criar isso, recebo a mensagem de erro: Erro: (49, 5) Nenhum recurso encontrado que corresponda ao nome dado: attr 'fontPath'.
585 Stef Stef

Tente adicionar xmlns:app="http://schemas.android.com/apk/res-auto"ao seu layout raiz, verifique a resposta atualizado também
norman784

O erro não vem do arquivo XML de layout, mas do arquivo XML de estilos. Parece que ele não 'sabe' o que é um fontPath.
Stef

O seu direito, se esqueceu de que eu tenho uma biblioteca chamada Calligrapy
norman784

1

A resposta de Peter é a melhor, mas pode ser aprimorada usando o styles.xml do Android para personalizar suas fontes para todas as visualizações de texto em seu aplicativo.

Meu código está aqui


1

Existem duas maneiras de personalizar fontes:

!!! minha fonte personalizada em assets / fonts / iran_sans.ttf

Caminho 1: Tipo de atualização.class ||| melhor maneira

chamada FontsOverride.setDefaultFont () na classe estende Application. Esse código fará com que todas as fontes de software sejam alteradas, mesmo que brinda fontes

AppController.java

public class AppController extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        //Initial Font
        FontsOverride.setDefaultFont(getApplicationContext(), "MONOSPACE", "fonts/iran_sans.ttf");

    }
}

FontsOverride.java

public class FontsOverride {

    public static void setDefaultFont(Context context, String staticTypefaceFieldName, String fontAssetName) {
        final Typeface regular = Typeface.createFromAsset(context.getAssets(), fontAssetName);
        replaceFont(staticTypefaceFieldName, regular);
    }

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

Caminho 2: use setTypeface

para uma visualização especial, basta chamar setTypeface () para alterar a fonte.

CTextView.java

public class CTextView extends TextView {

    public CTextView(Context context) {
        super(context);
        init(context,null);
    }

    public CTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context,attrs);
    }

    public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context,attrs);
    }

    public void init(Context context, @Nullable AttributeSet attrs) {

        if (isInEditMode())
            return;

        // use setTypeface for change font this view
        setTypeface(FontUtils.getTypeface("fonts/iran_sans.ttf"));

    }
}

FontUtils.java

public class FontUtils {

    private static Hashtable<String, Typeface> fontCache = new Hashtable<>();

    public static Typeface getTypeface(String fontName) {
        Typeface tf = fontCache.get(fontName);
        if (tf == null) {
            try {
                tf = Typeface.createFromAsset(AppController.getInstance().getApplicationContext().getAssets(), fontName);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            fontCache.put(fontName, tf);
        }
        return tf;
    }

}


0

Você pode criar facilmente uma classe de visualização de texto personalizada: -

Então, o que você precisa fazer primeiro, faça a Custom textviewclasse que foi estendida AppCompatTextView.

public class CustomTextView extends AppCompatTextView {
    private int mFont = FontUtils.FONTS_NORMAL;
    boolean fontApplied;

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

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

    public CustomTextView(Context context) {
        super(context);
        init(null, context);
    }

    protected void init(AttributeSet attrs, Context cxt) {
        if (!fontApplied) {
            if (attrs != null) {
                mFont = attrs.getAttributeIntValue(
                        "http://schemas.android.com/apk/res-auto", "Lato-Regular.ttf",
                        -1);
            }
            Typeface typeface = getTypeface();
            int typefaceStyle = Typeface.NORMAL;
            if (typeface != null) {
                typefaceStyle = typeface.getStyle();
            }
            if (mFont > FontUtils.FONTS) {
                typefaceStyle = mFont;
            }
            FontUtils.applyFont(this, typefaceStyle);
            fontApplied = true;
        }
    }
}

Agora, toda vez texto personalizado chamada vista e vamos obter o valor int de atributo int fontValue = attrs.getAttributeIntValue("http://schemas.android.com/apk/res-auto","Lato-Regular.ttf",-1).

Ou

Também podemos obter getTypeface () da exibição que definimos em nosso xml ( android:textStyle="bold|normal|italic"). Então faça o que você quiser.

Agora, criamos FontUtilspara definir qualquer fonte .ttf em nossa visualização.

public class FontUtils {

    public static final int FONTS = 1;
    public static final int FONTS_NORMAL = 2;
    public static final int FONTS_BOLD = 3;
    public static final int FONTS_BOLD1 = 4;

    private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();

    static Typeface getFonts(Context context, String name) {
        Typeface typeface = TYPEFACE.get(name);
        if (typeface == null) {
            typeface = Typeface.createFromAsset(context.getAssets(), name);
            TYPEFACE.put(name, typeface);
        }
        return typeface;
    }

    public static void applyFont(TextView tv, int typefaceStyle) {

        Context cxt = tv.getContext();
        Typeface typeface;

        if(typefaceStyle == Typeface.BOLD_ITALIC) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
        }else if (typefaceStyle == Typeface.BOLD || typefaceStyle == SD_FONTS_BOLD|| typefaceStyle == FONTS_BOLD1) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-SemiBold.ttf");
        } else if (typefaceStyle == Typeface.ITALIC) {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Thin.ttf");
        } else {
            typeface = FontUtils.getFonts(cxt, "FaktPro-Normal.ttf");
        }
        if (typeface != null) {
            tv.setTypeface(typeface);
        }
    }
}

0

Pode ser útil saber que, a partir do Android 8.0 (nível 26 da API), você pode usar uma fonte personalizada em XML .

Você pode aplicar uma fonte personalizada a todo o aplicativo da seguinte maneira.

  1. Coloque a fonte na pasta res/font.

  2. Em res/values/styles.xmluso no tema do aplicativo. <style name="AppTheme" parent="{whatever you like}"> <item name="android:fontFamily">@font/myfont</item> </style>



Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.