È possibile impostare un carattere personalizzato per l'intera applicazione?


270

Devo usare determinati caratteri per tutta la mia applicazione. Ho il file .ttf per lo stesso. È possibile impostare questo come carattere predefinito, all'avvio dell'applicazione e quindi utilizzarlo altrove nell'applicazione? Quando impostato, come lo uso nei miei XML di layout?


in Android Studio 3.0 puoi facilmente impostare caratteri personalizzati: developer.android.com/guide/topics/ui/look-and-feel/…
MHSFisher

@MHSFisher Fonts in XML è una funzionalità per Android 8.0 (livello API 26)
Emi Raz,

1
@EmiRaz, leggi il documento developer.android.com/guide/topics/ui/look-and-feel/… . questa funzione è stata aggiunta nella libreria di supporto 26, ma supporto dall'API 16.
MHSFisher

Risposte:


450

Sì con la riflessione. Funziona ( basato su questa risposta ):

(Nota: questa è una soluzione alternativa a causa della mancanza di supporto per i caratteri personalizzati, quindi se si desidera cambiare questa situazione, si prega di iniziare a votare qui il problema Android ). Nota: non lasciare commenti "anch'io" su questo problema, tutti coloro che l'hanno visto ricevono un'email quando lo fai. Quindi basta "star" per favore.

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

È quindi necessario sovraccaricare i pochi caratteri predefiniti, ad esempio in una classe di applicazione :

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

Oppure, se stai usando lo stesso file di font, puoi migliorarlo per caricarlo una sola volta.

Tuttavia, tendo a sovrascriverne uno, diciamo "MONOSPACE", quindi impostare uno stile per forzare l'applicazione di un carattere tipografico a livello:

<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

Ho esaminato i rapporti nei commenti che non funziona e sembra essere incompatibile con il tema android:Theme.Material.Light.

Se quel tema non è importante per te, usa un tema più vecchio, ad esempio:

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:typeface">monospace</item>
</style>

11
come sovrascriveresti monospace grassetto o corsivo?
Christopher Rivera,

2
@ChristopherRivera C'è "DEFAULT_BOLD"ma anche un campo privato: private static Typeface[] sDefaults;potresti provare ad accedervi riflettendo e impostando il sDefaults[Typeface.ITALIC]tuoTypeface. Vedi questo
Weston,

5
@weston Non importa. L'ho raggiunto. C'è stato un errore nella mia comprensione. Hai fatto un ottimo lavoro. Puoi dirmi una cosa? Perché non funziona per DEFAULT Typeface? Ho cambiato carattere tipografico in MONOSPACE come suggerimento e quindi applicare il login, ha funzionato. Ma non funziona per DEFAULT
Jay Pandya il

2
Non è possibile sbagliare il carattere tipografico nativo. Risolto, aggiungendo il percorso dei caratteri. "FontsOverride.setDefaultFont (this," DEFAULT "," fonts / MyFontAsset.ttf ");"
Sai

3
Trovo davvero fastidioso dover sostituire l'impostazione predefinita TextViewovunque per utilizzare caratteri personalizzati o utilizzare una soluzione basata sulla riflessione come questa. In combinazione con il set limitato di caratteri disponibile in Android, rende lo sviluppo di app di bell'aspetto un sacco di duro lavoro. Ho aggiunto una richiesta di funzionalità al tracker dei problemi Android. Se desideri visualizzare questa funzione, puoi aggiungere
Sam

64

Esiste una grande libreria per caratteri personalizzati in Android: Calligrafia

ecco un esempio su come usarlo.

in Gradle devi inserire questa riga nel file build.gradle della tua app:

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

e quindi creare una classe che si estende Applicatione scrivere questo codice:

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 nella classe di attività metti questo metodo prima di onCreate:

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

e l'ultima cosa che il tuo file manifest dovrebbe assomigliare a questo:

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

e cambierà l'intera attività sul tuo carattere! è semplice e pulito!


1
Questa è la soluzione perfetta.
nizam.sp,

1
È vero, la sua soluzione molto perfetta, in questo modo non devo scrivere classi diverse per aggiornare i caratteri per voci di menu, pulsante di opzione o casella di controllo. Si applica a tutti !! grazie :)
Manisha,

2
Sicuramente non è una soluzione perfetta: gli stili grassetto / corsivo / grassetto corsivo vengono sovrascritti. Al momento non esiste un modo semplice per impostare quelle famiglie.
0101100101,

2
Se lo faccio Come posso passare da grassetto a normale?
GMX

2
Per ottenere grassetto, corsivo o sottolineato, ho usato <b></b>, <u></u>e <i></i>nel strings.xmlfile e finora funziona.
jlively

47

Sebbene ciò non funzionerebbe per un'intera applicazione, funzionerebbe per un'attività e potrebbe essere riutilizzato per qualsiasi altra attività. Ho aggiornato il mio codice grazie a @ FR073N per supportare altre visualizzazioni. Non sono sicuro dei problemi con Buttons, RadioGroupsecc. Perché quelle classi si estendono tutte TextViewquindi dovrebbero funzionare bene. Ho aggiunto una condizione booleana per l'utilizzo della riflessione perché sembra molto hacker e potrebbe compromettere le prestazioni.

Nota: come sottolineato, questo non funzionerà per i contenuti dinamici! Per questo, è possibile chiamare questo metodo con dire un metodo onCreateViewo getView, ma richiede uno sforzo aggiuntivo.

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

Quindi per usarlo dovresti fare qualcosa del genere:

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

Spero che aiuti.


4
Penso che il modo migliore sarebbe quello di ignorare la visualizzazione del testo. Questo metodo potrebbe diventare in qualche modo ridondante se si dispone di un'applicazione con molte visualizzazioni.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
Potrebbe ... ma secondo le specifiche di Android vuoi evitare layout più profondi di 4 livelli e 100 visualizzazioni di larghezza (anche questo è un po '). La ricorsione può essere negativa in termini di prestazioni, ma anche se il layout era di 20 livelli non è nemmeno significativo.
Tom,

Concordo sul fatto che non ha un impatto notevole sulla prestazione e non sto cercando di dire che la tua risposta è sbagliata. Semplicemente non mi piace scorrere tutte le viste nel layout se non ne ho bisogno. Inoltre, estendendo TextView, se si desidera modificare l'aspetto in un altro modo, è necessario modificare il codice in un solo punto.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
Volevo solo aggiungere che il codice sopra riportato copre solo TextViews ma ListViews, Alerts, Toasts, Map Markers ecc. Continueranno a utilizzare il carattere di sistema.
csch

2
ragazzi questa funzione accetta 3 parametri, quando la chiamate ricorsivamente avete passato 2 parametri ??? qual è quella corretta? cosa è riflettere ??
MBH,

34

In sintesi:

Opzione n. 1: utilizzare la riflessione per applicare il carattere (combinando la risposta di 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();
            } 
        }
    }

} 

Utilizzo in classe di applicazione:

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

imposta uno stile per forzare l'applicazione di quel tipo di carattere (basata su lovefish ):

Pre-Lollipop:

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

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

Lecca-lecca (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>

Opzione2: sottoclasse ogni vista in cui è necessario personalizzare il carattere, ad es. ListView, EditTextView, Button, ecc. ( Risposta di 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);
    } 
} 

Opzione 3: implementare un crawler di visualizzazione che attraversa la gerarchia di visualizzazione della schermata corrente:

Variazione n. 1 ( risposta di 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... */ }
        } 
    } 
} 

Utilizzo:

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

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

Opzione n. 4: usa la lib di terze parti chiamata Calligrafia .

Personalmente, consiglierei l'opzione n. 4, in quanto consente di risparmiare un sacco di mal di testa.


In opzione # 1 , che cosa si intende per "sans-serif"a newMap.put(, e monospacein styles?! Voglio usare un carattere personalizzato chiamato barana.ttf.
Dr.jacky,

nell'opzione 1, il primo frammento di codice non funziona con il livello API 22, Android 5.1.1. Ma l'altra sezione del codice lo fa. Quale dovrebbe essere l'esatta condizione se così?
user1510006

28

Vorrei migliorare Weston 's risposta per API 21 Android 5.0.

Causa

Sotto API 21, la maggior parte degli stili di testo include l'impostazione fontFamily, come:

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

Che applica il carattere predefinito Roboto normale:

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

La risposta originale non riesce ad applicare il carattere monospace, perché android: fontFamily ha una priorità maggiore su android: attributo carattere ( riferimento ). L'uso di Theme.Holo. * È una soluzione alternativa valida, perché non ci sono impostazioni android: fontFamily all'interno.

Soluzione

Poiché Android 5.0 inserisce il carattere tipografico di sistema nella variabile statica Typeface.sSystemFontMap ( riferimento ), possiamo usare la stessa tecnica di riflessione per sostituirla:

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

4
bella soluzione per L., tuttavia, mi chiedo perché questo non sembra funzionare per i miei Buttontitoli s e Tool Bar. Sto sovrascrivendo il carattere predefinito in principale dell'applicazione come segue: FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. Sto usando Nexus 5, v5.1.1
kip2

3
Ehi, grazie È stato molto utile, solo una cosa: non funziona su Android 6 (API 22). Quindi il codice dovrebbe essere modificato in questo modo: Build.VERSION.SDK_INT == 21- Questo è solo nel API 21 test I su Nexus con API 22
Saeid

Non sono ancora in grado di impostare caratteri personalizzati usando questo metodo in Nexus 9
Rahul Upadhyay,

2
Chiunque abbia problemi a non funzionare per 5.1+. Non sovrascrivere il carattere tipografico negli stili valori-v21.
Muhammad Babar,

@MuhammadBabar Grazie per la tua soluzione mi ha reso
felice

15

è molto semplice ... 1.Scarica e metti il ​​tuo carattere personalizzato nelle risorse ... quindi scrivi una classe separata per la visualizzazione del testo come segue: qui ho usato il carattere 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);
    }
}

}

ed esegui le seguenti operazioni in 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"
      />

Sì, funziona :) Ma cosa succede se voglio applicare gli stessi caratteri ad altre viste / widget rimanenti? Devo scrivere anche una classe separata per tutte le altre viste (incluse finestre di dialogo / toast / barre delle azioni)?
Narendra Singh,

Sì, per questa soluzione è necessario scrivere classi separate per ciascuna vista.
Saad Bilal,

9

Vorrei anche suggerire di estendere TextView e altri controlli, ma sarebbe meglio considerare di impostare il carattere nei costrutti.

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

2
Fai attenzione quando esegui questa operazione su piattaforme precedenti alla 4.0: questo perderà molte risorse a causa di un bug in Android: code.google.com/p/android/issues/detail?id=9904
Ryan M

come possiamo ripristinare i valori predefiniti. Sto facendo qualcosa del genere: 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

Mi piacerebbe migliorare il Weston 's e Roger Huang ' s risposte per oltre 21 API lecca-lecca Android con il tema ' Theme.AppCompat '.

Sotto 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 5.0 (uguale)

<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 il FontsOverride util file è uguale a quello che in Weston 's risposta. Ho testato su questi telefoni:

Nexus 5 (Android 5.1 Sistema Android primario)

ZTE V5 (android 5.1 CM12.1)

Nota XIAOMI (Android 4.4 MIUI6)

HUAWEI C8850 (android 2.3.5 SCONOSCIUTO)


8

Una soluzione brillante può essere trovata qui: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Estendi semplicemente le attività da BaseActivity e scrivi quei metodi. Dovresti anche memorizzare meglio i caratteri nella cache come descritto qui: https://stackoverflow.com/a/16902532/2914140 .


Dopo alcune ricerche ho scritto un codice che funziona su Samsung Galaxy Tab A (Android 5.0). Codice usato di weston e Roger Huang e https://stackoverflow.com/a/33236102/2914140 . Testato anche su Lenovo TAB 2 A10-70L, dove non funziona. Ho inserito un carattere "Comic Sans" qui per vedere la differenza.

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

Per eseguire il codice nell'intera applicazione, è necessario scrivere in alcune classi come Applicazione quanto segue:

    new FontsOverride(this).loadFonts();

Crea una cartella "caratteri" all'interno di "risorse" e inserisci lì i caratteri necessari. Una semplice istruzione può essere trovata qui: https://stackoverflow.com/a/31697103/2914140 .

Inoltre, il dispositivo Lenovo ottiene erroneamente il valore di un carattere tipografico. Nella maggior parte dei casi restituisce Typeface.NORMAL, a volte nullo. Anche se un TextView è in grassetto (nel layout di file xml). Vedi qui: TextView isBold restituisce sempre NORMAL . In questo modo un testo su uno schermo è sempre in un carattere regural, non grassetto o corsivo. Quindi penso che sia un bug di un produttore.


Ho studiato il codice sorgente Android, la tua risposta è la migliore. Può sostituire sottile, medio, leggero o qualsiasi altra cosa. Grazie mille!
Mohammad Omidvar,

6

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

Implementazione dell'applicazione:

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

Stile:

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

6

A partire da Android O questo è ora possibile definire direttamente dall'XML e il mio bug è ora chiuso!

Vedi qui per i dettagli

TL; DR:

Per prima cosa devi aggiungere i tuoi caratteri al progetto

Secondo, aggiungi una famiglia di caratteri, in questo modo:

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

Infine, puoi utilizzare il carattere in un layout o stile:

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

Godere!


Grazie. Dovremmo usare Android Studio 2.4 per poter aggiungere la cartella dei caratteri.
Punto fotonico

in realtà ho solo bisogno di applicarlo al mio stile e verrà applicato attraverso l'applicazione. ma alcuni (i controlli aggiunti a livello di codice dovranno essere applicati tramite codice java)
SolidSnake,

4

Puoi impostare i caratteri personalizzati per ogni layout uno per uno, con una sola chiamata di funzione da ogni layout passando la sua vista principale. Innanzitutto, crea un approccio singelton per accedere all'oggetto carattere come questo

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

}

Puoi definire caratteri diversi nella classe precedente, ora Definisci una classe Helper di carattere che applicherà i caratteri:

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

        }

    }

}

Nel codice sopra, sto applicando i caratteri solo su textView e EditText, puoi applicare i caratteri anche su altri elementi della vista in modo simile. Devi solo passare l'id del tuo gruppo Vista radice al metodo di applicazione del carattere sopra riportato. ad esempio il 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>

Nel layout sopra, l'id principale del genitore è "Genitore principale" ora consente di applicare il carattere

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

Saluti :)


Bello .. non più ricreare lo stesso carattere, solo 1 carattere per tutti i campi. :)
Arfan Mirza,

3

Suggerirei di estendere TextView e di utilizzare sempre TextView personalizzato nei layout XML o ovunque sia necessario TextView. Nel tuo TextView personalizzato, esegui l'overridesetTypeface

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

2

La soluzione di Tom funziona alla grande, ma funziona solo con TextView e EditText.

Se vuoi coprire la maggior parte delle visualizzazioni (RadioGroup, TextView, Checkbox ...), ho creato un metodo per farlo:

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

Questo metodo ripristinerà lo stile della vista impostata in XML (grassetto, corsivo ...) e li applicherà se esistenti.

Per ListView, creo sempre un adattatore e imposto il carattere all'interno di getView.


2

Ho scritto una classe assegnando il carattere tipografico alle viste nella gerarchia delle viste corrente e basandomi sulle proprietà correnti del carattere tipografico (grassetto, normale, puoi aggiungere altri stili se vuoi):

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

Ora in tutti i frammenti di onViewCreated o onCreateView, in tutte le attività in onCreate e in tutti gli adattatori di visualizzazione in getView o newView basta invocare:

typefaceAssigner.assignTypeface(view);

2

in api 26 con build.gradle 3.0.0 e versioni successive puoi creare una directory di caratteri in res e usare questa linea nel tuo stile

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

per cambiare build.gradle usa questo nelle tue dipendenze build.gradle

classpath 'com.android.tools.build:gradle:3.0.0'

e come inserire il carattere nel progetto?
Azerafati,

@azerafati copia il tuo carattere in res / font
Mohammad Hosein Heidari

sì, tanx. L'ho già capito. Ma questo itemda solo non imposta il carattere per l'intera app. qualche indizio?
Azerafati,

1

Infine, Google ha compreso la gravità di questo problema (applicando font personalizzati ai componenti dell'interfaccia utente) e ha escogitato una soluzione pulita per questo.

Innanzitutto, è necessario aggiornare per supportare la libreria 26+ (potrebbe anche essere necessario aggiornare il proprio gradle {4.0+}, android studio), quindi è possibile creare una nuova cartella di risorse chiamata font. In questa cartella puoi inserire le risorse del tuo carattere (.tff, ...). Quindi devi sovrascrivere l'app predefinita e forzare il tuo carattere personalizzato in esso :)

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

Nota: se desideri supportare dispositivi con API precedenti a 16, devi utilizzare lo spazio dei nomi delle app anziché Android!


0

Vorrei anche migliorare la risposta di Weston per API 21 Android 5.0.

Ho avuto lo stesso problema sul mio Samsung s5, quando si utilizzava il carattere DEFAULT. (con gli altri caratteri funziona bene)

Sono riuscito a farlo funzionare impostando il carattere tipografico ("sans" per esempio) nei file XML, per ogni visualizzazione di testo o pulsante

<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 nella classe MyApplication:

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

Spero che sia d'aiuto.



0

Calligraphy funziona abbastanza bene, ma non è adatto a me, poiché non supporta pesi diversi (grassetto, corsivo, ecc.) Per una famiglia di caratteri.

Quindi ho provato Fontain , che consente di definire viste personalizzate e applicarle famiglie di caratteri personalizzate.

per utilizzare Fontain, è necessario aggiungere quanto segue al modulo dell'app build.gradle:

compile 'com.scopely:fontain:1.0.0'

Quindi, anziché utilizzare TextView normale, è necessario utilizzare FontTextView

Esempio di FontTextView con contenuto maiuscolo e grassetto:

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

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

    }

}

Non è una soluzione perfetta. Utilizzando questa soluzione richiederà soluzioni personalizzate per l'accesso di default Android TextViews
Blueware

0

Per modificare la famiglia di caratteri predefinita di TextViews, sovrascrivi textViewStyle nel tema dell'app.

Per utilizzare i caratteri personalizzati in fontFamily, utilizzare le risorse dei caratteri presenti nella libreria di supporto.

La funzione è stata aggiunta in Android 26 ma è stata eseguita il backport a versioni precedenti tramite 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 # utilizzando-support-lib


0

Dal rilascio di Android Oreo e della sua libreria di supporto (26.0.0) puoi farlo facilmente. Fare riferimento a questa risposta in un'altra domanda.

Fondamentalmente il tuo stile finale sarà simile a questo:

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


-15

Sì, è possibile impostare il carattere per l'intera applicazione.

Il modo più semplice per ottenere questo risultato è impacchettare i font desiderati con la tua applicazione.

Per fare ciò, è sufficiente creare una risorsa / cartella nella radice del progetto e inserire i caratteri (in TrueType, o TTF, modulo) nelle risorse.

Ad esempio, potresti creare risorse / font / e inserire i tuoi file 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);
}
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.