Ist es möglich, eine benutzerdefinierte Schriftart für die gesamte Anwendung festzulegen?

270

Ich muss für meine gesamte Anwendung eine bestimmte Schriftart verwenden. Ich habe eine .ttf-Datei für das gleiche. Ist es möglich, diese beim Start der Anwendung als Standardschriftart festzulegen und sie dann an einer anderen Stelle in der Anwendung zu verwenden? Wie verwende ich es in meinen Layout-XMLs, wenn es festgelegt ist?

Samuh
quelle
In Android Studio 3.0 können Sie ganz einfach benutzerdefinierte Schriftarten
festlegen
@MHSFisher Fonts in XML ist eine Funktion für Android 8.0 (API Level 26)
Emi Raz
1
@EmiRaz lesen Sie bitte das Dokument developer.android.com/guide/topics/ui/look-and-feel/… . Diese Funktion wurde in der Support-Bibliothek 26 hinzugefügt, aber von API 16 unterstützt.
MHSFisher

Antworten:

449

Ja mit Nachdenken. Dies funktioniert ( basierend auf dieser Antwort ):

(Hinweis: Dies ist eine Problemumgehung, da benutzerdefinierte Schriftarten nicht unterstützt werden. Wenn Sie diese Situation ändern möchten, stimmen Sie bitte ab, um das Android-Problem hier zu verbessern .) Hinweis: Hinterlassen Sie keine "Ich auch" -Kommentare zu diesem Thema. Jeder, der es angestarrt hat, erhält dabei eine E-Mail. Also bitte einfach "stern".

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

Sie müssen dann die wenigen Standardschriftarten überladen, beispielsweise in einer Anwendungsklasse :

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

Wenn Sie dieselbe Schriftartdatei verwenden, können Sie diese natürlich verbessern, um sie nur einmal zu laden.

Ich neige jedoch dazu, nur einen zu überschreiben "MONOSPACE"und dann einen Stil einzurichten, um die Anwendung dieser Schriftart zu erzwingen:

<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

Ich habe die Berichte in den Kommentaren untersucht, dass es nicht funktioniert und mit dem Thema nicht kompatibel zu sein scheint android:Theme.Material.Light .

Wenn Ihnen dieses Thema nicht wichtig ist, verwenden Sie ein älteres Thema, z.

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:typeface">monospace</item>
</style>
Weston
quelle
11
Wie würden Sie Monospace fett oder kursiv überschreiben?
Christopher Rivera
2
@ChristopherRivera Es gibt "DEFAULT_BOLD"aber auch ein privates Feld: private static Typeface[] sDefaults;Sie können versuchen, durch Reflektion und Einstellung sDefaults[Typeface.ITALIC]auf yourTypeface darauf zuzugreifen. Sehen Sie das.
Weston
5
@weston Macht nichts. Ich habe es erreicht. Es gab einen Fehler in meinem Verständnis. Sie haben einen tollen Job gemacht. Kannst du mir nur eins sagen? Warum funktioniert es nicht für DEFAULT Typeface? Ich habe Typeface als Ihren Vorschlag in MONOSPACE geändert und dann das Loginc angewendet, es hat funktioniert. Aber nicht für DEFAULT arbeiten
Jay Pandya
2
Native Schrift kann nicht fehlerhaft gemacht werden. Behoben durch Hinzufügen eines Schriftpfads. "FontsOverride.setDefaultFont (dies," DEFAULT "," fonts / MyFontAsset.ttf ");"
Sai
3
Ich finde es wirklich ärgerlich, TextViewüberall die Standardeinstellungen austauschen zu müssen, um benutzerdefinierte Schriftarten oder eine auf Reflexion basierende Lösung wie diese zu verwenden. In Kombination mit der begrenzten Anzahl von Schriftarten, die in Android verfügbar sind, macht die Entwicklung gut aussehender Apps viel harte Arbeit. Ich habe dem Android Issue Tracker eine Feature-Anfrage hinzugefügt. Wenn Sie diese Funktion sehen möchten
Sam
64

Es gibt eine großartige Bibliothek für benutzerdefinierte Schriftarten in Android: Kalligraphie

Hier ist ein Beispiel, wie man es benutzt.

In Gradle müssen Sie diese Zeile in die build.gradle-Datei Ihrer App einfügen:

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

und Applicationerstellen Sie dann eine Klasse, die diesen Code erweitert und schreibt:

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

und in der Aktivitätsklasse setzen Sie diese Methode vor onCreate:

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

und das Letzte, was Ihre Manifestdatei so aussehen sollte:

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

und es wird die gesamte Aktivität in Ihre Schriftart ändern! es ist einfach und sauber!

Shia G.
quelle
1
Dies ist die perfekte Lösung.
Nizam.sp
1
Richtig, es ist eine sehr perfekte Lösung. Auf diese Weise muss ich keine verschiedenen Klassen schreiben, um Schriftarten für Menüelemente, Optionsfelder oder Kontrollkästchen zu aktualisieren. Es gilt für alle !! danke :)
Manisha
2
Auf keinen Fall eine perfekte Lösung: Fett / kursiv / fett-kursiv werden überschrieben. Es gibt derzeit keine einfache Möglichkeit, diese Familien festzulegen.
0101100101
2
Wenn ich das mache Wie kann ich von Fett zu Normal wechseln?
GMX
2
Um fett, kursiv oder unterstrichen Feature zu bekommen, habe ich verwendet <b></b>, <u></u>und <i></i>in der strings.xmlDatei und so weit es funktioniert.
lebhaft
47

Dies würde zwar nicht für eine gesamte Anwendung funktionieren, aber für eine Aktivität und könnte für jede andere Aktivität wiederverwendet werden. Ich habe meinen Code dank @ FR073N aktualisiert, um andere Ansichten zu unterstützen. Ich bin nicht sicher über Probleme mit Buttons, RadioGroupsusw. , da diese Klassen alle erweiternTextView , so sollten sie gut funktionieren. Ich habe eine boolesche Bedingung für die Verwendung von Reflektion hinzugefügt, da diese sehr hackig erscheint und die Leistung erheblich beeinträchtigen kann.

Hinweis: Wie bereits erwähnt, funktioniert dies nicht für dynamische Inhalte! Dafür ist es möglich, diese Methode mit einer onCreateViewoder getViewMethode aufzurufen , erfordert jedoch zusätzlichen Aufwand.

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

Um es dann zu benutzen, würden Sie so etwas tun:

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

Hoffentlich hilft das.

Tom
quelle
4
Ich denke, der beste Weg wäre, die Textansicht zu überschreiben. Diese Methode kann etwas redundant werden, wenn Sie eine Anwendung mit vielen Ansichten haben.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
Es könnte ... aber gemäß den Android-Spezifikationen möchten Sie Layouts vermeiden, die tiefer als 4 Ebenen und 100 Ansichten breit sind (auch das ist ziemlich viel). Rekursion kann in Bezug auf die Leistung schlecht sein, aber selbst wenn Ihr Layout 20 Ebenen umfasst, ist dies nicht einmal von Bedeutung.
Tom
Ich würde zustimmen, dass es keinen spürbaren Einfluss auf die Leistung hat und ich versuche nicht zu sagen, dass Ihre Antwort falsch ist. Ich mag es einfach nicht, durch jede Ansicht im Layout zu radeln, wenn ich nicht muss. Wenn Sie TextView erweitern und das Erscheinungsbild auf andere Weise ändern möchten, müssen Sie den Code nur an einer Stelle ändern.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
Ich wollte nur hinzufügen, dass der obige Code nur TextViews abdeckt, aber ListViews, Alerts, Toasts, Map Markers usw. verwenden weiterhin die Systemschriftart.
csch
2
Leute, diese Funktion nimmt 3 Parameter, wenn du sie rekursiv aufrufst, hast du 2 Parameter übergeben ??? welches ist das richtige? Was ist reflektieren?
MBH
34

Zusammenfassend:

Option 1: Verwenden Sie Reflection, um die Schriftart anzuwenden (Kombination der Antwort von Weston & 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();
            } 
        }
    }

} 

Verwendung in der Anwendungsklasse:

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

Richten Sie einen Stil ein, um die Anwendung dieser Schriftart zu erzwingen (basierend auf 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>

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

Option 2: Unterklassen Sie jede Ansicht, in der Sie die Schriftart anpassen müssen, d. H. Listview, EditTextView, Knopf, usw. ( Palani ‚s Antwort):

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: Implementieren Sie einen View Crawler, der die Ansichtshierarchie Ihres aktuellen Bildschirms durchläuft:

Variante 1 ( Toms Antwort):

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

Verwendung :

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

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

Option 4: Verwenden Sie die Drittanbieter-Bibliothek namens Kalligraphie .

Persönlich würde ich Option 4 empfehlen, da dies viele Kopfschmerzen erspart.

Phileo99
quelle
Was meinen Sie in Option 1 mit "sans-serif"in newMap.put(und monospacein styles?! Ich möchte eine benutzerdefinierte Schriftart mit dem Namen verwenden barana.ttf.
Dr. Jacky
In Option 1 funktioniert das erste Code-Snippet nicht mit API Level 22, Android 5.1.1. Aber der andere Codeabschnitt tut es. Was sollte die genaue Wenn-Bedingung sein?
user1510006
28

Ich möchte Westons Antwort für API 21 Android 5.0 verbessern .

Ursache

Unter API 21 enthalten die meisten Textstile die Einstellung fontFamily wie:

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

Womit die Standardschrift Roboto Regular angewendet wird:

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

In der ursprünglichen Antwort wird die Monospace-Schriftart nicht angewendet, da android: fontFamily dem Attribut android: typeface ( Referenz) eine höhere Priorität einräumt ) hat. Die Verwendung von Theme.Holo. * Ist eine gültige Problemumgehung, da keine Einstellungen für android: fontFamily enthalten sind.

Lösung

Da Android 5.0 die Systemschrift in die statische Variable Typeface.sSystemFontMap ( Referenz ) einfügt , können wir dieselbe Reflexionstechnik verwenden, um sie zu ersetzen:

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
quelle
4
Gute Lösung für L. Ich frage mich jedoch, warum dies für meine Buttons- und Tool Bar-Titel nicht zu funktionieren scheint . Ich überschreibe die Standardschriftart in MainApplication wie folgt : FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. Ich benutze Nexus 5, v5.1.1
kip2
3
Hey, danke Das war sehr hilfreich, nur eines: Das funktioniert unter Android 6 (API 22) nicht. Dann sollte der Code folgendermaßen geändert werden: Build.VERSION.SDK_INT == 21- Das ist gerade im API 21 I-Test bei Nexus mit API 22
Saeid
Trotzdem kann ich mit dieser Methode in Nexus 9
Rahul Upadhyay
2
Jeder, der Probleme hat, nicht für 5.1+ zu arbeiten. Überschreiben Sie die Schrift in Ihren Werten-v21-Stilen nicht.
Muhammad Babar
@ MuhammadBabar Vielen Dank, dass Ihre Lösung meinen Tag gemacht hat
M. Yogeshwaran
15

Es ist sehr einfach ... 1.Downloaden Sie Ihre benutzerdefinierte Schriftart und fügen Sie sie in Assets ein. Dann schreiben Sie eine separate Klasse für die Textansicht wie folgt: Hier habe ich die Futura-Schriftart verwendet

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

}}

und machen Sie folgendes 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"
      />
Palani
quelle
Ja, das funktioniert :) Aber was ist, wenn ich dieselben Schriftarten für andere verbleibende Ansichten / Widgets anwenden möchte? Muss ich auch für alle anderen Ansichten (einschließlich Dialoge / Toasts / Aktionsleisten) eine separate Klasse schreiben?
Narendra Singh
Ja, für diese Lösung müssen Sie für jede Ihrer Ansichten separate Klassen schreiben.
Saad Bilal
9

Ich würde auch vorschlagen, TextView und andere Steuerelemente zu erweitern, aber es wäre besser, die Schriftart in Konstrukten einzurichten.

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
quelle
2
Seien Sie vorsichtig, wenn Sie dies auf Plattformen vor 4.0 tun - dies wird aufgrund eines Fehlers in Android viele Ressourcen verlieren
Ryan M
Wie können wir zum Standard zurückkehren? Ich mache ungefähr so: 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

Ich möchte die Antworten von Weston und Roger Huang für über API 21 Android Lollipop mit dem Thema " Theme.AppCompat " verbessern .

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

Über (gleich) 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>

Und die FontsOverride- Util-Datei entspricht der Antwort von Weston . Ich habe in diesen Handys getestet:

Nexus 5 (Android 5.1 Primäres Android-System)

ZTE V5 (Android 5.1 CM12.1)

XIAOMI Hinweis (Android 4.4 MIUI6)

HUAWEI C8850 (Android 2.3.5 UNBEKANNT)

Liebesfisch
quelle
8

Eine brillante Lösung finden Sie hier: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Erweitern Sie einfach die Aktivitäten von BaseActivity und schreiben Sie diese Methoden. Außerdem sollten Sie Schriftarten besser wie hier beschrieben zwischenspeichern: https://stackoverflow.com/a/16902532/2914140 .


Nach einigen Recherchen habe ich Code geschrieben, der auf dem Samsung Galaxy Tab A (Android 5.0) funktioniert. Verwendeter Code von Weston und Roger Huang sowie https://stackoverflow.com/a/33236102/2914140 . Auch auf Lenovo TAB 2 A10-70L getestet, wo es nicht funktioniert. Ich habe hier eine Schriftart 'Comic Sans' eingefügt, um einen Unterschied zu erkennen.

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

Um den Code in der gesamten Anwendung auszuführen, sollten Sie in einer Klasse wie Application Folgendes schreiben:

    new FontsOverride(this).loadFonts();

Erstellen Sie einen Ordner "Schriftarten" in "Assets" und legen Sie die erforderlichen Schriftarten dort ab. Eine einfache Anleitung finden Sie hier: https://stackoverflow.com/a/31697103/2914140 .

Das Lenovo Gerät erhält auch fälschlicherweise den Wert einer Schrift. In den meisten Fällen wird Typeface.NORMAL zurückgegeben, manchmal null. Auch wenn eine Textansicht fett gedruckt ist (im XML-Dateilayout). Siehe hier: TextView isBold gibt immer NORMAL zurück . Auf diese Weise ist ein Text auf einem Bildschirm immer in einer normalen Schriftart, nicht fett oder kursiv. Ich denke, es ist ein Fehler eines Produzenten.

CoolMind
quelle
Ich habe Android-Quellcode studiert, Ihre Antwort ist die beste beste. Es kann dünn, mittel, leicht oder irgendetwas anderes ersetzen. Ich danke dir sehr!
Mohammad Omidvar
6

Arbeiten für Xamarin.Android:

Klasse:

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

Anwendungsimplementierung:

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

Stil:

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

Ab Android O kann dies jetzt direkt aus dem XML definiert werden und mein Fehler ist jetzt geschlossen!

Siehe hier für Details

TL; DR:

Zuerst müssen Sie Ihre Schriftarten zum Projekt hinzufügen

Zweitens fügen Sie eine Schriftfamilie hinzu, wie folgt:

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

Schließlich können Sie die Schriftart in einem Layout oder Stil verwenden:

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

Genießen!

Sam
quelle
Vielen Dank. Wir sollten Android Studio 2.4 verwenden, um einen Schriftordner hinzufügen zu können.
Photon Point
Eigentlich muss ich es nur auf meinen Stil anwenden und es wird über die Anwendung angewendet. aber einige (programmgesteuert hinzugefügte Steuerelemente müssen über Java-Code angewendet werden)
SolidSnake
4

Sie können benutzerdefinierte Schriftarten für jedes Layout einzeln festlegen, indem Sie nur einen Funktionsaufruf von jedem Layout ausführen, indem Sie die Stammansicht übergeben. Erstellen Sie zunächst einen Singelton-Ansatz für den Zugriff auf ein solches Schriftartobjekt

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

}

Sie können verschiedene Schriftarten in der obigen Klasse definieren. Definieren Sie jetzt eine Schriftart-Hilfsklasse, die Schriftarten anwendet:

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

        }

    }

}

Im obigen Code wende ich Schriftarten nur auf textView und EditText an. Sie können Schriftarten auch auf andere Ansichtselemente anwenden. Sie müssen lediglich die ID Ihrer Stammansichtsgruppe an die oben beschriebene Methode zum Anwenden von Schriftarten übergeben. Zum Beispiel ist Ihr 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>

Im obigen Layout lautet die übergeordnete Stamm-ID "Hauptübergeordnet". Jetzt können Sie die Schriftart anwenden

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

Prost :)

Ajji
quelle
Schön .. keine gleiche Schriftart neu erstellen, nur 1 Schriftart für alle Felder verwenden. :)
Arfan Mirza
3

Ich würde vorschlagen, TextView zu erweitern und immer Ihre benutzerdefinierte TextView in Ihren XML-Layouts oder überall dort zu verwenden, wo Sie eine TextView benötigen. Überschreiben Sie in Ihrer benutzerdefinierten TextansichtsetTypeface

@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
quelle
2

Toms Lösung funktioniert hervorragend, funktioniert aber nur mit TextView und EditText.

Wenn Sie die meisten Ansichten (RadioGroup, TextView, Checkbox ...) abdecken möchten, habe ich folgende Methode erstellt:

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

Diese Methode gibt den Stil der in XML festgelegten Ansicht (fett, kursiv ...) zurück und wendet sie an, falls vorhanden.

Für die ListView erstelle ich immer einen Adapter und lege die Schriftart in getView fest.

FR073N
quelle
2

Ich habe eine Klasse geschrieben, die den Ansichten in der aktuellen Ansichtshierarchie eine Schriftart zuweist und auf den aktuellen Schrifteigenschaften basiert (fett, normal, Sie können andere Stile hinzufügen, wenn Sie möchten):

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

Jetzt rufen Sie in allen Fragmenten in onViewCreated oder onCreateView, in allen Aktivitäten in onCreate und in allen Ansichtsadaptern in getView oder newView einfach Folgendes auf:

typefaceAssigner.assignTypeface(view);
Ivan Kravchenko
quelle
2

In API 26 mit build.gradle 3.0.0 und höher können Sie ein Schriftverzeichnis in res erstellen und diese Zeile in Ihrem Stil verwenden

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

Verwenden Sie dies für change build.gradle in Ihren build.gradle-Abhängigkeiten

classpath 'com.android.tools.build:gradle:3.0.0'
Mohammad Hosein Heidari
quelle
und wie füge ich die Schriftart in das Projekt ein?
Azerafati
@azerafati kopieren Sie Ihre Schriftart in res / font
Mohammad Hosein Heidari
Ja, Tanx. Das habe ich schon bekommen. Dies itemallein legt jedoch nicht die Schriftart für die gesamte App fest. irgendeine Ahnung?
Azerafati
1

Schließlich erkannte Google die Schwere dieses Problems (Anwenden einer benutzerdefinierten Schriftart auf UI-Komponenten) und entwickelte eine saubere Lösung dafür.

Zuerst müssen Sie ein Update durchführen, um die Bibliothek 26+ zu unterstützen (möglicherweise müssen Sie auch Ihren Gradle {4.0+}, Android Studio, aktualisieren), dann können Sie einen neuen Ressourcenordner namens font erstellen. In diesem Ordner können Sie Ihre Schriftartenressourcen (.tff, ...) ablegen. Dann müssen Sie die Standard-App überschreiben und Ihre benutzerdefinierte Schriftart darin erzwingen :)

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

Hinweis: Wenn Sie Geräte mit einer älteren API als 16 unterstützen möchten, müssen Sie anstelle von Android den App-Namespace verwenden!

Mr.Q.
quelle
0

Ich möchte auch Westons Antwort für API 21 Android 5.0 verbessern.

Ich hatte das gleiche Problem auf meinem Samsung s5, als ich die DEFAULT-Schriftart verwendete. (mit den anderen Schriftarten funktioniert es gut)

Ich habe es geschafft, es zum Laufen zu bringen, indem ich die Schriftart (zum Beispiel "sans") in XML-Dateien für jede Textansicht oder Schaltfläche festgelegt habe

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

und in der MyApplication-Klasse:

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

Ich hoffe es hilft.

Stevenwood
quelle
0

Kalligraphie funktioniert ziemlich gut, ist aber für mich nicht geeignet, da sie für eine Schriftfamilie keine unterschiedlichen Gewichte (fett, kursiv usw.) unterstützt.

Also habe ich es mit Fontain versucht , mit dem Sie benutzerdefinierte Ansichten definieren und diese auf benutzerdefinierte Schriftfamilien anwenden können.

Um Fontain verwenden zu können, sollten Sie Ihrem App-Modul build.gradle Folgendes hinzufügen:

compile 'com.scopely:fontain:1.0.0'

Anstatt reguläres TextView zu verwenden, sollten Sie dann FontTextView verwenden

Beispiel für FontTextView mit Großbuchstaben und fettem Inhalt:

 <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"/>
Krise
quelle
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);

    }

}
Die EasyLearn Academy
quelle
Keine perfekte Lösung. Mit dieser Lösung wird benutzerdefinierte Abhilfen für den Zugriff Standard Android erfordern TextViews
Blue
0

Überschreiben Sie textViewStyle in Ihrem App-Design, um die Standardschriftfamilie der TextViews zu ändern.

Verwenden Sie für die Verwendung benutzerdefinierter Schriftarten in fontFamily die Schriftartenressourcen, die sich in der Unterstützungsbibliothek befinden.

Die Funktion wurde in Android 26 hinzugefügt, aber über supportlib auf ältere Versionen zurückportiert.

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

Seit der Veröffentlichung von Android Oreo und seiner Support-Bibliothek (26.0.0) können Sie dies problemlos tun. Beziehen Sie sich auf diese Antwort in einer anderen Frage.

Grundsätzlich sieht Ihr endgültiger Stil folgendermaßen aus:

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

Ja, es ist möglich, die Schriftart für die gesamte Anwendung festzulegen.

Der einfachste Weg, dies zu erreichen, besteht darin, die gewünschten Schriftarten in Ihre Anwendung zu packen.

Erstellen Sie dazu einfach ein Assets / einen Ordner im Projektstamm und fügen Sie Ihre Schriftarten (in TrueType- oder TTF-Form) in die Assets ein.

Sie können beispielsweise Assets / Fonts / erstellen und Ihre TTF-Dateien dort ablegen.

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
quelle