Est-il possible de définir une police personnalisée pour l'ensemble de l'application?

270

J'ai besoin d'utiliser certaines polices pour toute mon application. J'ai un fichier .ttf pour le même. Est-il possible de définir cela comme police par défaut, au démarrage de l'application, puis de l'utiliser ailleurs dans l'application? Une fois défini, comment puis-je l'utiliser dans mes XML de mise en page?

Samuh
la source
dans android studio 3.0, vous pouvez facilement définir des polices personnalisées: developer.android.com/guide/topics/ui/look-and-feel/…
MHSFisher
@MHSFisher Fonts en XML est une fonctionnalité pour Android 8.0 (API niveau 26)
Emi Raz
1
@EmiRaz veuillez lire la doc developer.android.com/guide/topics/ui/look-and-feel/… . cette fonctionnalité a été ajoutée dans la bibliothèque de support 26, mais elle est prise en charge par l'API 16.
MHSFisher

Réponses:

450

Oui avec réflexion. Cela fonctionne ( basé sur cette réponse ):

(Remarque: il s'agit d'une solution de contournement en raison du manque de prise en charge des polices personnalisées, donc si vous souhaitez changer cette situation, veuillez commencer à voter pour le problème Android ici ). Remarque: Ne laissez pas de commentaires "moi aussi" sur ce problème, tous ceux qui l'ont regardé reçoivent un e-mail lorsque vous faites cela. Donc, "star" s'il vous plaît.

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

Vous devez ensuite surcharger les quelques polices par défaut, par exemple dans une classe d' application :

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 bien sûr, si vous utilisez le même fichier de police, vous pouvez l'améliorer pour le charger une seule fois.

Cependant, j'ai tendance à en remplacer un, disons "MONOSPACE", puis à configurer un style pour forcer cette application de police de caractères à l'échelle:

<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

J'ai enquêté sur les rapports dans les commentaires qu'il ne fonctionne pas et il semble être incompatible avec le thème android:Theme.Material.Light.

Si ce thème n'est pas important pour vous, utilisez un thème plus ancien, par exemple:

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:typeface">monospace</item>
</style>
weston
la source
11
comment remplaceriez-vous les espaces monospaces en gras ou en italique?
Christopher Rivera
2
@ChristopherRivera Il y a "DEFAULT_BOLD"mais aussi un champ privé: private static Typeface[] sDefaults;vous pouvez essayer d'y accéder en réfléchissant et en paramétrant votre sDefaults[Typeface.ITALIC]Typeface. Regardez ça.
Weston
5
@weston Tant pis. Je l'ai atteint. Il y avait une erreur dans ma compréhension. Tu as fais un bon travail. Peux-tu juste me dire une chose? Pourquoi cela ne fonctionne-t-il pas pour la police par défaut? J'ai changé Typeface en MONOSPACE comme suggestion, puis j'applique la connexion, cela a fonctionné. Mais ne travaille pas pour DEFAULT
Jay Pandya
2
La police de caractères native ne peut pas faire d'erreur. Corrigé, en ajoutant le chemin des polices. "FontsOverride.setDefaultFont (this," DEFAULT "," fonts / MyFontAsset.ttf ");"
Sai
3
Je trouve vraiment ennuyeux de devoir remplacer la valeur par défaut TextViewpartout pour utiliser des polices personnalisées ou utiliser une solution basée sur la réflexion comme celle-ci. Combiné avec l'ensemble limité de polices disponibles dans Android, il est difficile de développer de belles applications. J'ai ajouté une demande de fonctionnalité au traqueur de problèmes Android. Si vous souhaitez voir cette fonctionnalité, vous pouvez signaler
Sam
64

Il existe une excellente bibliothèque de polices personnalisées dans Android: Calligraphie

voici un exemple d'utilisation.

dans Gradle, vous devez mettre cette ligne dans le fichier build.gradle de votre application:

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

puis créez une classe qui étend Applicationet écrivez ce code:

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

et dans la classe d'activité, mettez cette méthode avant onCreate:

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

et la dernière chose que votre fichier manifeste devrait ressembler à ceci:

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

et cela changera toute l'activité en votre police! c'est simple et propre!

Shia G
la source
1
C'est la solution parfaite.
nizam.sp
1
Certes, sa solution très parfaite, de cette façon, je n'ai pas à écrire différentes classes pour mettre à jour les polices des éléments de menu, des boutons radio ou des cases à cocher. Cela s'applique à tous !! merci :)
Manisha
2
Certainement pas une solution parfaite: les styles gras / italique / gras-italique sont remplacés. Il n'existe actuellement aucun moyen facile de définir ces familles.
0101100101
2
Si je fais cela Comment puis-je passer du gras au régulier?
GMX
2
Pour obtenir la fonctionnalité gras, italique ou souligné, j'ai utilisé <b></b>, <u></u>et <i></i>dans le strings.xmlfichier et jusqu'à présent, cela fonctionne.
jlively
47

Bien que cela ne fonctionnerait pas pour une application entière, cela fonctionnerait pour une activité et pourrait être réutilisé pour toute autre activité. J'ai mis à jour mon code grâce à @ FR073N pour prendre en charge d'autres vues. Je ne suis pas sûr des problèmes avec Buttons, RadioGroupsetc., car ces classes s'étendent toutes TextViewet devraient donc fonctionner correctement. J'ai ajouté une condition booléenne pour l'utilisation de la réflexion car elle semble très hackeuse et pourrait notamment compromettre les performances.

Remarque: comme indiqué, cela ne fonctionnera pas pour le contenu dynamique! Pour cela, il est possible d'appeler cette méthode avec disons une méthode onCreateViewou getView, mais nécessite un effort supplémentaire.

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

Ensuite, pour l'utiliser, vous feriez quelque chose comme ceci:

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

J'espère que cela pourra aider.

À M
la source
4
Je pense que la meilleure façon serait de remplacer textview. Cette méthode pourrait devenir quelque peu redondante si vous avez une application avec beaucoup de vues.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
Cela pourrait ... mais selon les spécifications d'Android, vous voulez éviter les mises en page plus profondes que 4 niveaux et 100 vues larges (même c'est un peu). La récursivité peut être mauvaise en termes de performances, mais même si votre disposition était de 20 niveaux, ce n'est même pas significatif.
Tom
Je conviens que cela n'a pas d'impact notable sur la performance et je n'essaie pas de dire que votre réponse est fausse. Je n'aime tout simplement pas parcourir toutes les vues de la mise en page si je n'en ai pas besoin. De plus, en étendant TextView, si vous souhaitez modifier l'apparence d'une autre manière, vous n'aurez qu'à modifier le code à un endroit.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
Je voulais juste ajouter que le code ci-dessus ne couvre que les TextViews mais ListViews, Alertes, Toasts, Map Markers etc. utiliseront toujours la police système.
csch
2
les gars cette fonction prend 3 paramètres, quand vous l'appelez récursivement vous avez passé 2 paramètres ??? quelle est la bonne? qu'est-ce qui se reflète ??
MBH
34

En résumé:

Option 1: Utilisez la réflexion pour appliquer la police (en combinant la réponse de Weston et 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();
            } 
        }
    }

} 

Utilisation dans la classe Application:

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

configurer un style pour forcer cette application de police de caractères à l'échelle (basée sur lovefish ):

Pré-sucette:

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

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

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

Option2: sous - classe chaque vue où vous devez personnaliser la police, par exemple. ListView, EditTextView, Button, etc. ( réponse 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);
    } 
} 

Option 3: implémentez un robot d'exploration qui parcourt la hiérarchie des vues de votre écran actuel:

Variation # 1 ( réponse 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... */ }
        } 
    } 
} 

Utilisation:

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

Variation # 2: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Option n ° 4: utilisez une bibliothèque tierce appelée calligraphie .

Personnellement, je recommanderais l'option n ° 4, car elle évite beaucoup de maux de tête.

Phileo99
la source
Dans l' option n ° 1 , qu'entendez-vous par "sans-serif"dans newMap.put(et monospacedans styles?! Je souhaite utiliser une police personnalisée nommée barana.ttf.
Dr.jacky
dans l'option 1, le premier extrait de code ne fonctionne pas avec l'API niveau 22, Android 5.1.1. Mais l'autre section de code le fait. Quelle devrait être l'exacte condition si?
user1510006
28

Je voudrais améliorer la réponse de weston pour API 21 Android 5.0.

Cause

Sous API 21, la plupart des styles de texte incluent le paramètre fontFamily, comme:

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

Qui applique la police Roboto Regular par défaut:

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

La réponse d'origine ne parvient pas à appliquer la police monospace, car android: fontFamily a une priorité plus élevée sur l'attribut android: typeface ( référence ). L'utilisation de Theme.Holo. * Est une solution de contournement valide, car il n'y a pas de paramètres android: fontFamily à l'intérieur.

Solution

Étant donné qu'Android 5.0 place la police système dans la variable statique Typeface.sSystemFontMap ( référence ), nous pouvons utiliser la même technique de réflexion pour la remplacer:

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
la source
4
belle solution pour L. cependant, je me demande pourquoi cela ne semble pas fonctionner pour mes Buttontitres s et Tool Bar. Je redéfinissant la police par défaut dans MainApplication comme suit: FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. J'utilise Nexus 5, v5.1.1
kip2
3
Hé, merci Cela a été très utile, juste une chose: cela ne fonctionne pas sur Android 6 (API 22). Ensuite, le code devrait être modifié comme suit: Build.VERSION.SDK_INT == 21- C'est juste dans le API 21 test I chez Nexus avec API 22
Saeid
Je ne suis toujours pas en mesure de définir une police personnalisée à l'aide de cette méthode dans Nexus 9
Rahul Upadhyay
2
Toute personne ayant un problème ne fonctionne pas pour 5.1+. Ne remplacez pas la police de caractères dans vos styles values-v21.
Muhammad Babar
@MuhammadBabar Merci de votre solution a fait ma journée
M.Yogeshwaran
15

son très simple ... 1.Téléchargez et mettez votre police personnalisée ur dans les actifs ... puis écrivez une classe distincte pour la vue texte comme suit: ici j'ai utilisé la police 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);
    }
}

}

et procédez comme suit en 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
la source
Oui, cela fonctionne :) Mais, que se passe-t-il si je veux appliquer les mêmes polices pour les autres vues / widgets restants? Dois-je également écrire une classe distincte pour toutes les autres vues (y compris les boîtes de dialogue / toasts / barres d'action)?
Narendra Singh
Oui, pour cette solution, vous devez écrire des classes distinctes pour chacune de vos vues.
Saad Bilal
9

Je suggérerais également d'étendre TextView et d'autres contrôles, mais il serait préférable d'envisager de configurer la police dans les constructions.

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
la source
2
Soyez prudent lorsque vous faites cela sur des plates-formes antérieures à 4.0 - cela entraînera une fuite de beaucoup de ressources en raison d'un bogue dans Android: code.google.com/p/android/issues/detail?id=9904
Ryan M
comment pouvons-nous revenir à la valeur par défaut. Je fais quelque chose comme ceci: if (configShared.getString ("storeId", AppCredentials.DEFAULT_STORE_ID) .equals ("2")) {final Field staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (true); staticField.set (null, newTypeface); } else {final Field staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (true); staticField.set (null, null); }
Killer
8

Je voudrais améliorer les réponses de Weston et Roger Huang pour plus de sucette Android 21 API avec le thème " Theme.AppCompat ".

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

Plus (égal) API 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>

Et le fichier util FontsOverride est le même que celui de la réponse de Weston . J'ai testé dans ces téléphones:

Nexus 5 (Android 5.1 Primary Android System)

ZTE V5 (android 5.1 CM12.1)

Note XIAOMI (android 4.4 MIUI6)

HUAWEI C8850 (android 2.3.5 INCONNU)

Lovefish
la source
8

Une solution brillante peut être trouvée ici: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Étendez simplement les activités de BaseActivity et écrivez ces méthodes. Vous devriez également améliorer les polices de cache comme décrit ici: https://stackoverflow.com/a/16902532/2914140 .


Après quelques recherches, j'ai écrit du code qui fonctionne sur Samsung Galaxy Tab A (Android 5.0). Code utilisé de Weston et Roger Huang ainsi que https://stackoverflow.com/a/33236102/2914140 . Également testé sur Lenovo TAB 2 A10-70L, où cela ne fonctionne pas. J'ai inséré une police 'Comic Sans' ici afin de voir une différence.

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

Pour exécuter le code dans toute l'application, vous devez écrire dans une classe comme Application ce qui suit:

    new FontsOverride(this).loadFonts();

Créez un dossier «polices» dans «actifs» et placez-y les polices nécessaires. Une instruction simple peut être trouvée ici: https://stackoverflow.com/a/31697103/2914140 .

Le périphérique Lenovo obtient également de manière incorrecte une valeur de police. Dans la plupart des cas, il renvoie Typeface.NORMAL, parfois null. Même si un TextView est en gras (dans une disposition de fichier xml). Voir ici: TextView isBold renvoie toujours NORMAL . De cette façon, un texte sur un écran est toujours dans une police regural, pas en gras ou en italique. Je pense donc que c'est un bug d'un producteur.

CoolMind
la source
J'ai étudié le code source Android, votre réponse est la meilleure et la meilleure. Il peut remplacer mince, moyen, léger ou autre. Merci beaucoup!
Mohammad Omidvar
6

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

Mise en œuvre de l'application:

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

Style:

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

Depuis Android O, il est maintenant possible de définir directement à partir du XML et mon bug est maintenant fermé!

Voir ici pour plus de détails

TL; DR:

Vous devez d'abord ajouter vos polices au projet

Ensuite, vous ajoutez une famille de polices, comme ceci:

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

Enfin, vous pouvez utiliser la police dans une mise en page ou un style:

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

Prendre plaisir!

Sam
la source
Merci. Nous devrions utiliser Android Studio 2.4 pour pouvoir ajouter un dossier de polices.
Photon Point
en fait, je dois juste l'appliquer à mon style et il sera appliqué via l'application. mais certains (les contrôles ajoutés par programmation devront être appliqués via le code java)
SolidSnake
4

Vous pouvez définir des polices personnalisées pour chaque mise en page une par une, avec un seul appel de fonction de chaque mise en page en passant sa vue racine. Créez d'abord une approche singelton pour accéder aux objets de police comme celui-ci

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

}

Vous pouvez définir différentes polices dans la classe ci-dessus, définissez maintenant une classe d'assistance de police qui appliquera les polices:

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

        }

    }

}

Dans le code ci-dessus, j'applique uniquement des polices sur textView et EditText, vous pouvez également appliquer des polices sur d'autres éléments de vue.Il vous suffit de transmettre l'ID de votre groupe de vues racine à la méthode de police d'application ci-dessus. par exemple, votre mise en page est:

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

Dans la disposition ci-dessus, l'ID du parent racine est "Parent principal" permet maintenant d'appliquer la police

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

À votre santé :)

Ajji
la source
Nice .. plus de même police à recréer, une seule police à utiliser pour tous les champs. :)
Arfan Mirza
3

Je suggère d'étendre TextView et de toujours utiliser votre TextView personnalisé dans vos mises en page XML ou partout où vous avez besoin d'un TextView. Dans votre TextView personnalisé, remplacezsetTypeface

@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
la source
2

La solution de Tom fonctionne très bien, mais ne fonctionne qu'avec TextView et EditText.

Si vous souhaitez couvrir la plupart des vues (RadioGroup, TextView, Checkbox ...), j'ai créé une méthode faisant cela:

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

Cette méthode récupérera le style de la vue définie en XML (gras, italique ...) et les appliquera s'ils existent.

Pour le ListView, je crée toujours un adaptateur et je définis la police dans getView.

FR073N
la source
2

J'ai écrit une classe attribuant la police de caractères aux vues dans la hiérarchie de vues actuelle et basée sur les propriétés de police de caractères actuelles (gras, normal, vous pouvez ajouter d'autres styles si vous le souhaitez):

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

Maintenant, dans tous les fragments dans onViewCreated ou onCreateView, dans toutes les activités dans onCreate et dans tous les adaptateurs de vue dans getView ou newView, appelez simplement:

typefaceAssigner.assignTypeface(view);
Ivan Kravchenko
la source
2

dans l'api 26 avec build.gradle 3.0.0 et supérieur, vous pouvez créer un répertoire de polices dans res et utiliser cette ligne dans votre style

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

pour changer build.gradle utilisez ceci dans vos dépendances build.gradle

classpath 'com.android.tools.build:gradle:3.0.0'
Mohammad Hosein Heidari
la source
et comment insérer la police dans le projet?
azerafati
@azerafati copiez votre police dans res / font
Mohammad Hosein Heidari
ouais, tanx. J'ai déjà compris ça. Mais cela itemseul ne définit pas la police de l'application entière. un indice?
azerafati
1

Enfin, Google a réalisé la gravité de ce problème (application d'une police personnalisée aux composants de l'interface utilisateur) et ils ont conçu une solution propre pour cela.

Tout d'abord, vous devez mettre à jour pour prendre en charge la bibliothèque 26+ (vous devrez peut-être également mettre à jour votre gradle {4.0+}, studio Android), puis vous pouvez créer un nouveau dossier de ressources appelé police. Dans ce dossier, vous pouvez mettre vos ressources de polices (.tff, ...). Ensuite, vous devez remplacer l'application par défaut et y forcer votre police personnalisée :)

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

Remarque: si vous souhaitez prendre en charge des appareils avec une API plus ancienne que 16, vous devez utiliser l'espace de noms d'application au lieu d'Android!

Mr.Q
la source
0

Je voudrais également améliorer la réponse de weston pour l'API 21 Android 5.0.

J'ai eu le même problème sur mon Samsung s5, lors de l'utilisation de la police DEFAULT. (avec les autres polices ça marche bien)

J'ai réussi à le faire fonctionner en définissant la police ("sans" par exemple) dans les fichiers XML, pour chaque 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" />

et dans MyApplication Class:

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

J'espère que ça aide.

Stevenwood
la source
0

Calligraphie fonctionne plutôt bien, mais elle ne me convient pas, car elle ne prend pas en charge différents poids (gras, italique, etc.) pour une famille de polices.

J'ai donc essayé Fontain , qui vous permet de définir des vues personnalisées et de leur appliquer des familles de polices personnalisées.

afin d'utiliser Fontain, vous devez ajouter ce qui suit à votre module d'application build.gradle:

compile 'com.scopely:fontain:1.0.0'

Ensuite, au lieu d'utiliser TextView standard, vous devez utiliser FontTextView

Exemple de FontTextView avec du contenu en majuscules et en gras:

 <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
la source
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);

    }

}
L'Académie EasyLearn
la source
Pas une solution parfaite. L' utilisation de cette solution nécessite des solutions de contournement personnalisées à défaut d'accès Android TextViews
Blueware
0

Pour modifier la famille de polices par défaut des TextViews, remplacez textViewStyle dans le thème de votre application.

Pour utiliser une police personnalisée dans fontFamily, utilisez les ressources de police qui se trouvent dans la bibliothèque de support.

La fonctionnalité a été ajoutée dans Android 26 mais rétroportée vers les anciennes versions 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
la source
0

Depuis la sortie d'Android Oreo et de sa bibliothèque de support (26.0.0), vous pouvez le faire facilement. Reportez-vous à cette réponse dans une autre question.

Fondamentalement, votre style final ressemblera à ceci:

<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
la source
-15

Oui, il est possible de définir la police pour l'ensemble de l'application.

La façon la plus simple de procéder consiste à empaqueter la ou les polices souhaitées avec votre application.

Pour ce faire, créez simplement un fichier / dossier à la racine du projet et placez vos polices (au format TrueType ou TTF) dans les fichiers.

Vous pouvez, par exemple, créer des éléments / polices / et y placer vos fichiers TTF.

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