É possível definir uma fonte personalizada para todo o aplicativo?

270

Preciso usar certa fonte para todo o meu aplicativo. Eu tenho o arquivo .ttf para o mesmo. É possível definir isso como fonte padrão, na inicialização do aplicativo e depois usá-lo em outro lugar no aplicativo? Quando definido, como usá-lo em meus XMLs de layout?

Samuh
fonte
no android studio 3.0, você pode definir facilmente fontes personalizadas: developer.android.com/guide/topics/ui/look-and-feel/…
MHSFisher
@MHSFisher fontes em XML é uma característica para Android 8.0 (nível API 26)
Emi Raz
1
@EmiRaz, leia o documento developer.android.com/guide/topics/ui/look-and-feel/… . esse recurso adicionado na biblioteca de suporte 26, mas o apoio da API 16.
MHSFisher

Respostas:

450

Sim com reflexão. Isso funciona (com base nesta resposta ):

(Observação: esta é uma solução alternativa devido à falta de suporte para fontes personalizadas; portanto, se você quiser alterar essa situação, faça uma votação positiva para o problema do Android aqui ). Nota: Não deixe comentários "eu também" sobre esse problema; todos os que o visualizaram receberão um email quando você fizer isso. Então, basta "estrelar", por favor.

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

Você precisa sobrecarregar as poucas fontes padrão, por exemplo, em uma classe de aplicativo :

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

Ou, é claro, se você estiver usando o mesmo arquivo de fonte, poderá melhorar isso para carregá-lo apenas uma vez.

No entanto, costumo substituir um, digamos "MONOSPACE", e configurar um estilo para forçar esse aplicativo de tipo de fonte:

<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

Investiguei os relatórios nos comentários de que ele não funciona e parece incompatível com o tema android:Theme.Material.Light.

Se esse tema não for importante para você, use um tema mais antigo, por exemplo:

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:typeface">monospace</item>
</style>
weston
fonte
11
como você substitui o monospace em negrito ou itálico?
23414 Christopher Rivera
2
@ChristopherRivera Existe "DEFAULT_BOLD"também um campo privado: private static Typeface[] sDefaults;você pode tentar acessar isso refletindo e definindo o seu tipo sDefaults[Typeface.ITALIC]de letra. Veja isso.
weston 27/03
5
@weston Não importa. Eu consegui isso. Houve um erro no meu entendimento. Você fez um ótimo trabalho. Você pode me dizer uma coisa? Por que ele não está funcionando para o DEFAULT Typeface? Alterei Typeface para MONOSPACE como sua sugestão e, em seguida, apliquei o loginc, funcionou. Mas não funciona por falta
Jay Pandya
2
Tipo de letra nativo não pode ser cometido erro. Corrigido, adicionando o caminho das fontes. "FontsOverride.setDefaultFont (this," DEFAULT "," fonts / MyFontAsset.ttf ");"
Sai
3
Acho realmente irritante ter que trocar o padrão em TextViewtodos os lugares para usar fontes personalizadas ou usar uma solução baseada em reflexão como esta. Combinado com o conjunto limitador de fontes disponíveis no Android, torna o desenvolvimento de aplicativos de boa aparência muito trabalhoso. Adicionei uma solicitação de recurso ao rastreador de problemas do Android. Se você quiser ver esse recurso, poderá marcar
Sam
64

Há uma ótima biblioteca de fontes personalizadas no Android: Caligrafia

Aqui está um exemplo de como usá-lo.

Em Gradle, você precisa colocar esta linha no arquivo build.gradle do seu aplicativo:

dependencies {
    compile 'uk.co.chrisjenx:calligraphy:2.2.0'
}

e faça uma classe que estenda Applicatione escreva este código:

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

e na classe de atividade, coloque esse método antes de onCreate:

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

e a última coisa que seu arquivo de manifesto deve ter esta aparência:

<application
   .
   .
   .
   android:name=".App">

e isso mudará toda a atividade para sua fonte! é simples e limpo!

Shia G
fonte
1
Esta é a solução perfeita.
Nsq.sp
1
É verdade que é uma solução muito perfeita. Dessa forma, não preciso escrever classes diferentes para atualizar fontes de itens de menu, botões de opção ou fotos de caixas de seleção. Aplica-se a todos !! Obrigado :)
Manisha
2
Definitivamente, não é uma solução perfeita: os estilos em negrito / itálico / negrito-itálico são substituídos. Atualmente, não há uma maneira fácil de definir essas famílias.
0101100101
2
Se eu fizer isso Como posso mudar de Negrito para Regular?
GMX
2
Para obter recurso negrito, itálico ou sublinhado, eu usei <b></b>, <u></u>e <i></i>no strings.xmlarquivo e até agora ele funciona.
jlively
47

Enquanto isso não funcionaria para um aplicativo inteiro, funcionaria para uma Atividade e poderia ser reutilizado para qualquer outra Atividade. Atualizei meu código graças a @ FR073N para oferecer suporte a outros modos de exibição. Não tenho certeza sobre problemas com Buttons, RadioGroupsetc. porque todas essas classes se estendem TextViewpara que funcionem bem. Eu adicionei uma condicional booleana para o uso da reflexão, porque parece muito imprudente e pode comprometer notavelmente o desempenho.

Nota: como indicado, isso não funcionará para conteúdo dinâmico! Para isso, é possível chamar esse método com say an onCreateViewou getViewmethod, mas requer esforço adicional.

/**
 * 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... */ }
        }
    }
}

Então, para usá-lo, você faria algo assim:

final Typeface mFont = Typeface.createFromAsset(getAssets(),
"fonts/MyFont.ttf"); 
final ViewGroup mContainer = (ViewGroup) findViewById(
android.R.id.content).getRootView();
HomeActivity.setAppFont(mContainer, mFont);

Espero que ajude.

Tom
fonte
4
Eu acho que a melhor maneira seria substituir a visualização de texto. Esse método pode ficar um pouco redundante se você tiver um aplicativo com muitas visualizações.
Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
Poderia ... mas de acordo com as especificações do Android, você deseja evitar layouts mais profundos que 4 níveis e 100 visualizações de largura (até isso é um pouco). A recursão pode ser ruim em termos de desempenho, mas mesmo que seu layout tenha 20 níveis, isso não é significativo.
Tom
Concordo que isso não tem um impacto perceptível no desempenho e não estou tentando dizer que sua resposta está errada. Só não gosto de percorrer todas as visualizações do layout, se não preciso. Além disso, estendendo o TextView, se você quiser modificar a aparência de outra maneira, precisará alterar apenas o código em um único local.
Zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
Só queria acrescentar que o código acima apenas cobre TextViews, mas ListViews, Alertas, Brindes, Map Markers etc. ainda usarão a fonte do sistema.
Csch
2
galera essa função leva 3 parâmetros, quando você chama recursivamente você passou 2 parâmetros ??? qual é o correto? o que é refletir ??
MBH
34

Em suma:

Opção 1: use a reflexão para aplicar a fonte (combinando a resposta de weston e 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();
            } 
        }
    }

} 

Uso na classe Aplicativo:

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

configure um estilo para forçar esse aplicativo de tipo de fonte (com base no lovefish ):

Pré-pirulito:

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

   <!-- Application theme. -->
   <style name="AppTheme" parent="AppBaseTheme">
       <item name="android:typeface">monospace</item>
   </style>
</resources>

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

Opção2: Subclasse toda e qualquer visualização em que você precise personalizar a fonte, por exemplo. ListView, EditTextView, Button, etc. ( resposta de Palani ):

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

Opção 3: implemente um rastreador de exibição que atravessa a hierarquia de exibição da tela atual:

Variação # 1 ( resposta de Tom ):

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... */ }
        } 
    } 
} 

Uso:

final ViewGroup mContainer = (ViewGroup) findViewById(
android.R.id.content).getRootView();
HomeActivity.setAppFont(mContainer, Typeface.createFromAsset(getAssets(),
"fonts/MyFont.ttf"));

Variação 2: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Opção 4: Use Lib de terceiros chamada Caligrafia .

Pessoalmente, eu recomendaria a opção 4, pois economiza muitas dores de cabeça.

Phileo99
fonte
Na opção 1 , o que você quer dizer com "sans-serif"in newMap.put(e monospacein styles?! Eu quero usar uma fonte personalizada chamada barana.ttf.
Dr.jacky
na opção 1, o primeiro snippet de código não funciona com o nível 22 da API, Android 5.1.1. Mas a outra seção de código sim. Qual deve ser a condição exata exata?
user1510006
28

Gostaria de melhorar a resposta da weston para a API 21 Android 5.0.

Causa

Na API 21, a maioria dos estilos de texto inclui a configuração fontFamily, como:

<style name="TextAppearance.Material">
     <item name="fontFamily">@string/font_family_body_1_material</item>
</style>

O que aplica a fonte padrão Roboto Regular:

<string name="font_family_body_1_material">sans-serif</string>

A resposta original falha ao aplicar a fonte monoespaçada, porque android: fontFamily tem maior prioridade ao atributo android: tipo de letra ( referência ). O uso de Theme.Holo. * É uma solução válida, porque não há configurações android: fontFamily dentro.

Solução

Como o Android 5.0 coloca a fonte do sistema na variável estática Typeface.sSystemFontMap ( referência ), podemos usar a mesma técnica de reflexão para substituí-la:

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();
        }
    }
}
Roger Huang
fonte
4
boa solução alternativa para L., no entanto, estou me perguntando por que isso não parece funcionar para meus Buttontítulos de barra de ferramentas. Eu estou substituindo a fonte padrão no MainApplication da seguinte forma: FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. Estou usando o Nexus 5, v5.1.1
kip2
3
Ei, obrigado Isso foi muito útil, apenas uma coisa: isso não funciona no Android 6 (API 22). Em seguida, o código deve ser alterado para isso: Build.VERSION.SDK_INT == 21- Isso é apenas no API 21 teste I no Nexus com API 22
Saeid
Ainda que eu não sou capaz de fonte personalizada conjunto usando este método no Nexus 9
Rahul Upadhyay
2
Qualquer um que tenha problemas para não trabalhar para o 5.1+. Não substitua a fonte nos estilos values-v21.
Muhammad Babar
@MuhammadBabar Obrigado sua solução fez o meu dia
M.Yogeshwaran
15

é muito simples ... 1. Faça o download e coloque sua fonte personalizada em assets..thent, escreva uma classe separada para a exibição de texto da seguinte maneira: aqui eu usei fonte 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);
    }
}

}

e faça o seguinte em 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"
      />
Palani
fonte
Sim, isso funciona :) Mas, e se eu quiser aplicar as mesmas fontes para outras visualizações / widgets restantes? Também preciso escrever uma classe separada para todas as outras visualizações (incluindo diálogos / brindes / barras de ação)?
Narendra Singh
Sim, para esta solução, você precisa escrever classes separadas para cada uma das suas visualizações.
Saad Bilal
9

Eu também sugeriria estender o TextView e outros controles, mas seria melhor considerar configurar a fonte nas construções.

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));
}
Andrey Mischenko
fonte
2
Tenha cuidado ao fazer isso em plataformas anteriores à 4.0 - isso vazará
Ryan M
como podemos voltar ao padrão. Estou fazendo algo assim: if (configShared.getString ("storeId", AppCredentials.DEFAULT_STORE_ID) .equals ("2")) {campo final staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (true); staticField.set (null, newTypeface); } else {campo final staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (true); staticField.set (nulo, nulo); }
Killer
8

Gostaria de melhorar as respostas de weston e Roger Huang para mais de pirulito Android API 21 com o tema " Theme.AppCompat ".

Abaixo do 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 sobre (igual) 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>

E o arquivo utilitário FontsOverride é o mesmo que na resposta de weston . Eu testei nestes telefones:

Nexus 5 (sistema Android primário do Android 5.1)

ZTE V5 (android 5.1 CM12.1)

Nota XIAOMI (android 4.4 MIUI6)

HUAWEI C8850 (android 2.3.5 DESCONHECIDO)

peixe-amor
fonte
8

Uma solução brilhante pode ser encontrada aqui: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Simplesmente estenda as atividades do BaseActivity e escreva esses métodos. Além disso, você deve armazenar em cache melhor as fontes, conforme descrito aqui: https://stackoverflow.com/a/16902532/2914140 .


Após algumas pesquisas, escrevi um código que funciona no Samsung Galaxy Tab A (Android 5.0). Código usado de weston e Roger Huang, bem como https://stackoverflow.com/a/33236102/2914140 . Também testado no Lenovo TAB 2 A10-70L, onde não funciona. Eu inseri uma fonte 'Comic Sans' aqui para ver a diferença.

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

Para executar o código em todo o aplicativo, você deve escrever em alguma classe como Application, o seguinte:

    new FontsOverride(this).loadFonts();

Crie uma pasta 'fontes' dentro de 'ativos' e coloque as fontes necessárias lá. Uma instrução simples pode ser encontrada aqui: https://stackoverflow.com/a/31697103/2914140 .

O dispositivo Lenovo também obtém incorretamente o valor de um tipo de letra. Na maioria das vezes, ele retorna Typeface.NORMAL, às vezes nulo. Mesmo se um TextView estiver em negrito (no layout do arquivo xml). Veja aqui: TextView isBold sempre retorna NORMAL . Dessa forma, um texto na tela está sempre em uma fonte regular, não em negrito ou itálico. Então eu acho que é um bug de um produtor.

CoolMind
fonte
Estudei o código fonte do Android, sua resposta é a melhor. Pode substituir fino, médio, leve ou qualquer outra coisa. Muito obrigado!
Mohammad Omidvar
6

Trabalhando para o Xamarin.Android:

Classe:

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

Implementação de aplicativo:

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

Estilo:

<style name="Theme.Storehouse" parent="Theme.Sherlock">
    <item name="android:typeface">monospace</item>
</style>
Guy Micciche
fonte
6

A partir do Android O, agora é possível definir diretamente a partir do XML e meu bug está encerrado!

Veja aqui para detalhes

TL; DR:

Primeiro você deve adicionar suas fontes ao projeto

Segundo, você adiciona uma família de fontes, assim:

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

Por fim, você pode usar a fonte em um layout ou estilo:

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

Aproveitar!

Sam
fonte
Obrigado. Devemos usar o Android Studio 2.4 para adicionar pastas de fontes.
Photon Point
na verdade, só preciso aplicá-lo ao meu estilo e ele será aplicado através do aplicativo. mas alguns (controles programaticamente adicionado terá de ser aplicada por meio de código java)
Solid Snake
4

Você pode definir fontes personalizadas para cada layout, uma por uma, com apenas uma chamada de função de cada layout, passando sua raiz View.First, crie uma abordagem de singelton para acessar o objeto de fonte como este

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

}

Você pode definir fontes diferentes na classe acima. Agora, defina uma classe auxiliar de fonte que aplicará fontes:

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

        }

    }

}

No código acima, estou aplicando fontes somente no textView e no EditText, você pode aplicar fontes em outros elementos de exibição também. Você só precisa passar o ID do seu grupo de exibição raiz para o método de fonte acima aplicado. por exemplo, seu layout é:

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

No layout acima, o ID principal raiz é "Principal pai" agora permite aplicar fonte

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

Felicidades :)

Ajji
fonte
Bom .. não há mais a mesma fonte recriada, apenas 1 fonte usada para todos os campos. :)
Arfan Mirza
3

Sugiro estender o TextView e sempre usar o TextView personalizado em seus layouts XML ou onde você precisar de um TextView. No seu TextView personalizado, substituasetTypeface

@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);
}
Sam Dozor
fonte
2

A solução de Tom funciona muito bem, mas funciona apenas com TextView e EditText.

Se você deseja cobrir a maioria das visualizações (RadioGroup, TextView, Checkbox ...), criei um método fazendo isso:

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

Este método recuperará o estilo da exibição definida em XML (negrito, itálico ...) e os aplicará, se existirem.

Para o ListView, eu sempre crio um adaptador e defino a fonte dentro de getView.

FR073N
fonte
2

Eu escrevi uma classe atribuindo o tipo de letra às visualizações na hierarquia de visualizações atual e com base nas propriedades atuais (negrito, normal, você pode adicionar outros estilos, se desejar):

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

Agora, em todos os fragmentos em onViewCreated ou onCreateView, em todas as atividades em onCreate e em todos os adaptadores de exibição em getView ou newView, basta chamar:

typefaceAssigner.assignTypeface(view);
Ivan Kravchenko
fonte
2

na API 26 com build.gradle 3.0.0 e superior, você pode criar um diretório de fontes em res e usar esta linha em seu estilo

<item name="android:fontFamily">@font/your_font</item>

para alterar build.gradle, use isso em suas dependências build.gradle

classpath 'com.android.tools.build:gradle:3.0.0'
Mohammad Hosein Heidari
fonte
e como inserir a fonte no projeto?
azerafati
@azerafati copie sua fonte para res / font
Mohammad Hosein Heidari
sim, tanx. Eu já entendi isso. Mas isso por itemsi só não define a fonte para todo o aplicativo. qualquer pista?
azerafati
1

Por fim, o Google percebeu a gravidade desse problema (aplicando fonte personalizada aos componentes da interface do usuário) e criou uma solução limpa para ele.

Primeiro, você precisa atualizar para dar suporte à biblioteca 26+ (também pode ser necessário atualizar seu gradle {4.0+}, android studio) e depois criar uma nova pasta de recursos chamada fonte. Nesta pasta, você pode colocar seus recursos de fonte (.tff, ...). Em seguida, você precisa substituí-los pelo aplicativo padrão e forçar sua fonte personalizada para ele :)

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

Nota: se você deseja oferecer suporte a dispositivos com API anterior a 16, é necessário usar o namespace do aplicativo em vez do android!

Mr.Q
fonte
0

Também gostaria de melhorar a resposta da weston para a API 21 Android 5.0.

Eu tive o mesmo problema no meu Samsung s5, ao usar a fonte DEFAULT. (com as outras fontes, está funcionando bem)

Consegui fazê-lo funcionar definindo o tipo de letra ("sans", por exemplo) em arquivos XML, para cada Textview ou Button

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

e na classe MyApplication:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
    TypefaceUtil.overrideFont(getApplicationContext(), "SANS_SERIF",
    "fonts/my_font.ttf");
    }
}

Espero que ajude.

stevenwood
fonte
0

A caligrafia funciona muito bem, mas não é adequada para mim, pois não suporta pesos diferentes (negrito, itálico etc.) para uma família de fontes.

Então, tentei o Fontain , que permite definir Visualizações personalizadas e aplicá-las a famílias de fontes personalizadas.

para usar o Fontain, você deve adicionar o seguinte ao seu módulo de aplicativo build.gradle:

compile 'com.scopely:fontain:1.0.0'

Em vez de usar o TextView comum, você deve usar FontTextView

Exemplo de FontTextView com conteúdo em maiúscula e negrito:

 <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"/>
Cris
fonte
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);

    }

}
A Academia EasyLearn
fonte
Não é uma solução perfeita. Usando esta solução vai exigir soluções personalizadas para acesso padrão Android TextViews
blueware
0

Para alterar a família de fontes padrão dos TextViews, substitua textViewStyle no tema do seu aplicativo.

Para usar fontes personalizadas em fontFamily, use os recursos de fonte que estão na biblioteca de suporte.

O recurso foi adicionado no Android 26, mas suportado para versões mais antigas via 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

Siyamed
fonte
0

Desde o lançamento do Android Oreo e sua biblioteca de suporte (26.0.0), você pode fazer isso facilmente. Consulte esta resposta em outra pergunta.

Basicamente, seu estilo final ficará assim:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
   <item name="fontFamily">@font/your_font</item> <!-- target android sdk versions < 26 and > 14 -->
</style>
João Magalhães
fonte
-15

Sim, é possível definir a fonte para todo o aplicativo.

A maneira mais fácil de fazer isso é empacotar a (s) fonte (s) desejada (s) com seu aplicativo.

Para fazer isso, basta criar uma pasta / ativos na raiz do projeto e colocar suas fontes (no formato TrueType ou TTF) nos ativos.

Você pode, por exemplo, criar ativos / fontes / e colocar seus arquivos TTF lá.

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