Czy można ustawić niestandardową czcionkę dla całej aplikacji?

270

Muszę używać określonej czcionki dla całej mojej aplikacji. Mam plik .ttf na to samo. Czy można ustawić tę czcionkę domyślną podczas uruchamiania aplikacji, a następnie użyć jej w innym miejscu aplikacji? Po ustawieniu, jak używać go w plikach XML układu?

Samuh
źródło
w Android Studio 3.0 możesz łatwo ustawić niestandardowe czcionki: developer.android.com/guide/topics/ui/look-and-feel/…
MHSFisher
@MHSFisher Fonts in XML to funkcja dla Androida 8.0 (API poziom 26)
Emi Raz
1
@EmiRaz proszę przeczytać dokument developer.android.com/guide/topics/ui/look-and-feel/… . ta funkcja została dodana do biblioteki pomocy 26, ale wsparcie z API 16.
MHSFisher

Odpowiedzi:

449

Tak z refleksją. To działa ( na podstawie tej odpowiedzi ):

(Uwaga: jest to obejście ze względu na brak obsługi niestandardowych czcionek, więc jeśli chcesz zmienić tę sytuację, zrób gwiazdkę, aby głosować tutaj na problem z Androidem ). Uwaga: nie zostawiaj komentarzy „ja też” na ten temat, wszyscy, którzy się na nią gapili, otrzymają wiadomość e-mail. Poprostu „gwiazdkuj”.

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

Następnie musisz przeładować kilka domyślnych czcionek, na przykład w klasie aplikacji :

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

Lub oczywiście, jeśli używasz tego samego pliku czcionek, możesz to poprawić, aby załadować go tylko raz.

Jednak staram się po prostu przesłonić jeden, powiedzmy "MONOSPACE", a następnie ustawić styl, aby wymusić na aplikacji szeroką czcionkę:

<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

Sprawdziłem raporty w komentarzach, że to nie działa i wydaje się być niezgodne z tematem android:Theme.Material.Light.

Jeśli ten motyw nie jest dla Ciebie ważny, użyj starszego motywu, np .:

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:typeface">monospace</item>
</style>
weston
źródło
11
jak zastąpiłbyś pogrubienie lub kursywę w Monospace?
Christopher Rivera
2
@ChristopherRivera Istnieje "DEFAULT_BOLD"również pole prywatne: private static Typeface[] sDefaults;możesz spróbować uzyskać do niego dostęp poprzez refleksję i ustawienie sDefaults[Typeface.ITALIC]swojegoTypeface. Zobacz to.
weston
5
@weston Nieważne. Osiągnąłem to. W moim rozumieniu był błąd. Wykonałeś świetną robotę. Czy możesz mi tylko powiedzieć jedną rzecz? Dlaczego nie działa dla DOMYŚLNEGO kroju? Zmieniłem czcionkę na MONOSPACE jako twoją sugestię, a następnie zastosowałem loginc, zadziałało. Ale nie działa dla DEFAULT
Jay Pandya
2
Nie można popełnić błędu w natywnym kroju pisma. Naprawiono, dodając ścieżkę czcionek. „FontsOverride.setDefaultFont (to,„ DOMYŚLNE ”,„ fonts / MyFontAsset.ttf ”);”
Sai
3
Uważam, że to naprawdę denerwujące, że muszę TextViewwszędzie wymieniać domyślne, aby użyć niestandardowych czcionek lub zastosować takie rozwiązanie oparte na odbiciu. W połączeniu z ograniczającym zestawem czcionek dostępnych w Androidzie sprawia, że ​​tworzenie dobrze wyglądających aplikacji jest ciężką pracą. Dodałem żądanie funkcji do narzędzia do śledzenia problemów w Androidzie. Jeśli chcesz zobaczyć tę funkcję, możesz oznaczyć ten problem gwiazdką tutaj: code.google.com/p/android/issues/…
Sam
64

W Androidzie jest świetna biblioteka niestandardowych czcionek: Kaligrafia

Oto próbka jak go użyć.

w Gradle musisz umieścić tę linię w pliku build.gradle aplikacji:

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

a następnie utwórz klasę rozszerzającą Applicationi napisz ten kod:

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

i w klasie aktywności umieść tę metodę przed onCreate:

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

i ostatnia rzecz, jaką powinien wyglądać twój plik manifestu:

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

i zmieni całą aktywność na twoją czcionkę! to proste i czyste!

Shia G.
źródło
1
To idealne rozwiązanie.
nizam.sp
1
To prawda, że ​​jest to bardzo idealne rozwiązanie, dzięki czemu nie muszę pisać różnych klas, aby zaktualizować czcionki dla pozycji menu, przycisku opcji lub fotns pola wyboru. Dotyczy wszystkich !! dzięki :)
Manisha,
2
Zdecydowanie nie jest to idealne rozwiązanie: style pogrubione / kursywa / pogrubione-kursywa są zastępowane. Obecnie nie ma łatwego sposobu na ustawienie tych rodzin.
0101100101
2
Jeśli to zrobię Jak mogę zmienić Pogrubienie na Zwykłe?
GMX,
2
Aby uzyskać pogrubienie, kursywa lub podkreślenie funkcji użyłem <b></b>, <u></u>a <i></i>w strings.xmlpliku i do tej pory działa.
dniu
47

Chociaż nie działałoby to w przypadku całej aplikacji, działałoby w przypadku działania i może być ponownie użyte w przypadku każdego innego działania. Zaktualizowałem swój kod dzięki @ FR073N, aby obsługiwać inne widoki. Nie jestem pewien co do problemów z Buttons, RadioGroupsitp., Ponieważ wszystkie te klasy rozszerzają się, TextViewwięc powinny działać dobrze. Dodałem wartość logiczną warunkującą użycie odbicia, ponieważ wydaje się bardzo hackerska i może znacząco pogorszyć wydajność.

Uwaga: jak wskazano, nie będzie to działać w przypadku treści dynamicznych! W tym celu można wywołać tę metodę za pomocą powiedzmy metodę onCreateViewlub getView, ale wymaga dodatkowego wysiłku.

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

Następnie, aby go użyć, zrobiłbyś coś takiego:

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

Mam nadzieję, że to pomaga.

Tomek
źródło
4
Myślę, że najlepszym sposobem byłoby zastąpienie widoku tekstu. Ta metoda może być nieco zbędna, jeśli masz aplikację z wieloma widokami.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
Może ... ale zgodnie ze specyfikacjami Androida chcesz unikać układów głębszych niż 4 poziomy i szerokie 100 widoków (nawet to całkiem sporo). Rekurencja może być zła pod względem wydajności, ale nawet jeśli układ miał 20 poziomów, nie jest to nawet znaczące.
Tom
Zgadzam się, że nie ma to zauważalnego wpływu na występy i nie próbuję powiedzieć, że twoja odpowiedź jest błędna. Po prostu nie lubię przechodzić przez każdy widok w układzie, jeśli nie muszę. Ponadto, rozszerzając TextView, jeśli chcesz zmodyfikować wygląd w inny sposób, musisz zmienić kod tylko w jednym miejscu.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
Chciałem tylko dodać, że powyższy kod obejmuje tylko TextViews, ale ListViews, Alerty, Toasty, Markery Map itp. Nadal będą używać czcionki systemowej.
csch
2
chłopaki, ta funkcja przyjmuje 3 parametry, kiedy wywołujesz ją rekurencyjnie, przeszedłeś 2 parametry ??? który jest poprawny? co to odzwierciedla?
MBH
34

W podsumowaniu:

Opcja nr 1: Użyj refleksji, aby zastosować czcionkę (łącząc odpowiedź Westona i Rogera Huanga ):

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

} 

Zastosowanie w klasie aplikacji:

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

ustaw styl, aby wymusić szeroką czcionkę tej aplikacji (w oparciu o lovefisha ):

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>

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

Opcja 2: Podklasuj każdy widok, w którym musisz dostosować czcionkę, tj. ListView, EditTextView, Button itp. (Odpowiedź 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);
    } 
} 

Opcja 3: Zaimplementuj widok przeszukiwacza, który przechodzi przez hierarchię widoków bieżącego ekranu:

Wariant nr 1 (odpowiedź Toma ):

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

Stosowanie :

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

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

Opcja # 4: Użyj Lib trzeciej strony o nazwie Kaligrafia .

Osobiście poleciłbym opcję nr 4, ponieważ oszczędza ona wielu bólów głowy.

Phileo99
źródło
W Opcji nr 1 , co masz na myśli przez "sans-serif"in newMap.put(i monospacein styles?! Chcę użyć niestandardowej czcionki o nazwie barana.ttf.
Dr.jacky
w opcji 1 pierwszy fragment kodu nie działa z interfejsem API w wersji 22, Android 5.1.1. Ale robi to druga sekcja kodu. Jaki powinien być dokładny, jeśli warunek tak?
user1510006
28

Chciałbym poprawić odpowiedź Westona dla API 21 Android 5.0.

Przyczyna

W API 21 większość stylów tekstowych zawiera ustawienie fontFamily, takie jak:

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

Który stosuje domyślną czcionkę Roboto Regular:

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

Oryginalna odpowiedź nie stosuje czcionki Monospace, ponieważ android: fontFamily ma większy priorytet niż android: atrybut kroju czcionki ( odwołanie ). Korzystanie z Theme.Holo. * Jest poprawnym obejściem, ponieważ nie ma w nim ustawień Androida: fontFamily.

Rozwiązanie

Ponieważ Android 5.0 umieścił krój pisma systemowego w zmiennej statycznej Typeface.sSystemFontMap ( odniesienie ), możemy zastosować tę samą technikę odbicia, aby go zastąpić:

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
źródło
4
fajne obejście dla L. Zastanawiam się jednak, dlaczego to nie działa w przypadku moich Buttontytułów i pasków narzędzi. Jestem przesłanianie domyślną czcionkę w MainApplication następująco: FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. Korzystam z Nexusa 5, wer. 5.1.1
kip2
3
Hej, dziękuję. To była bardzo pomocna, tylko jedna rzecz: to nie działa w Androidzie 6 (API 22). Następnie należy zmienić kod na: Build.VERSION.SDK_INT == 21- To tylko w API 21 teście I w Nexusie z API 22
Saeid
Nadal nie jestem w stanie ustawić niestandardowej czcionki za pomocą tej metody w Nexusie 9
Rahul Upadhyay,
2
Każdy, kto ma problem, nie działa w wersji 5.1+. Nie zastępuj kroju pisma w stylach wartości-v21.
Muhammad Babar
@MuhammadBabar Dzięki twoje rozwiązanie uczyniło mój dzień
M.Yogeshwaran
15

jego bardzo proste ... 1. Pobierz i umieść swoją niestandardową czcionkę w zasobach .. następnie napisz jedną osobną klasę do wyświetlania tekstu w następujący sposób: tutaj użyłem czcionki 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);
    }
}

}

i wykonaj następujące czynności w 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
źródło
Tak, to działa :) Ale co jeśli chcę zastosować te same czcionki do innych pozostałych widoków / widżetów? Czy muszę również pisać osobną klasę dla wszystkich innych widoków (w tym okien dialogowych / toastów / pasków akcji)?
Narendra Singh,
Tak, dla tego rozwiązania musisz napisać osobne klasy dla każdego swojego widoku.
Saad Bilal,
9

Sugerowałbym również rozszerzenie TextView i innych formantów, ale lepiej byłoby rozważyć ustawienie czcionki w konstrukcjach.

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
źródło
2
Bądź ostrożny, robiąc to na platformach wcześniejszych niż 4.0 - spowoduje to wyciek wielu zasobów z powodu błędu w Androidzie: code.google.com/p/android/issues/detail?id=9904
Ryan M
jak możemy przywrócić ustawienia domyślne. Robię coś takiego: 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

Chciałbym poprawić odpowiedzi Westona i Rogera Huanga na ponad API Lollipop dla Androida 21 z motywem „ Theme.AppCompat ”.

Poniżej Androida 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>

Ponad (równy) 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>

A FontsOverride util pliku to samo, co w Weston „s odpowiedź. Testowałem w tych telefonach:

Nexus 5 (podstawowy system Android 5.1)

ZTE V5 (Android 5.1 CM12.1)

Uwaga XIAOMI (Android 4.4 MIUI6)

HUAWEI C8850 (Android 2.3.5 UNKNOWN)

lovefish
źródło
8

Świetne rozwiązanie można znaleźć tutaj: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Po prostu rozszerz działania z BaseActivity i napisz te metody. Powinieneś także lepiej buforować czcionki, jak opisano tutaj: https://stackoverflow.com/a/16902532/2914140 .


Po kilku badaniach napisałem kod, który działa w Samsung Galaxy Tab A (Android 5.0). Użyto kodu weston i Roger Huang oraz https://stackoverflow.com/a/33236102/2914140 . Przetestowano również na Lenovo TAB 2 A10-70L, gdzie nie działa. Wstawiłem tutaj czcionkę „Comic Sans”, aby zobaczyć różnicę.

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

Aby uruchomić kod w całej aplikacji, powinieneś napisać w klasie, np. Application, co następuje:

    new FontsOverride(this).loadFonts();

Utwórz folder „czcionek” w „zasobach” i umieść tam potrzebne czcionki. Prostą instrukcję można znaleźć tutaj: https://stackoverflow.com/a/31697103/2914140 .

Urządzenie Lenovo również niepoprawnie otrzymuje wartość kroju pisma. W większości przypadków zwraca czcionkę Typ.NORMALNY, czasem null. Nawet jeśli TextView jest pogrubiony (w układzie pliku xml). Zobacz tutaj: TextView isBold zawsze zwraca NORMAL . W ten sposób tekst na ekranie ma zawsze czcionkę reguralną, a nie pogrubioną lub pochyloną. Myślę więc, że to błąd producenta.

CoolMind
źródło
Studiowałem kod źródłowy Androida, Twoja odpowiedź jest najlepsza. Może zastąpić cienki, średni, lekki lub cokolwiek innego. Dziękuję bardzo!
Mohammad Omidvar,
6

Praca dla Xamarin.Android:

Klasa:

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

Implementacja aplikacji:

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

Styl:

<style name="Theme.Storehouse" parent="Theme.Sherlock">
    <item name="android:typeface">monospace</item>
</style>
Guy Micciche
źródło
6

Od Androida O można teraz definiować bezpośrednio z pliku XML, a mój błąd jest teraz zamknięty!

Zobacz tutaj, aby uzyskać szczegółowe informacje

TL; DR:

Najpierw musisz dodać czcionki do projektu

Po drugie dodajesz rodzinę czcionek, na przykład:

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

Wreszcie możesz użyć czcionki w układzie lub stylu:

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

Cieszyć się!

Sam
źródło
Dzięki. Powinniśmy użyć Androida Studio 2.4, aby móc dodać folder czcionek.
Photon Point
właściwie po prostu muszę go zastosować do mojego stylu i zostanie on zastosowany w aplikacji. ale niektóre (kontrole dodane programowo będą musiały zostać zastosowane za pomocą kodu Java)
SolidSnake
4

Możesz ustawić niestandardowe czcionki dla każdego układu jeden po drugim, za pomocą tylko jednego wywołania funkcji z każdego układu, przekazując jego główny widok. Po pierwsze, stwórz pojedyncze podejście do dostępu do obiektu czcionki takiego

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

}

Możesz zdefiniować różne czcionki w powyższej klasie. Teraz zdefiniuj klasę pomocnika czcionek, która zastosuje czcionki:

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

        }

    }

}

W powyższym kodzie stosuję czcionki tylko do textView i EditText, możesz również stosować czcionki do innych elementów widoku podobnie. Musisz tylko przekazać identyfikator swojej głównej grupy widoków do powyższej metody stosowania czcionek. na przykład twój układ to:

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

W powyższym układzie głównym elementem nadrzędnym jest „Główny element nadrzędny”, który pozwala teraz zastosować czcionkę

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

Twoje zdrowie :)

Ajji
źródło
Fajnie .. nie trzeba już tworzyć tej samej czcionki, tylko 1 czcionka dla wszystkich pól. :)
Arfan Mirza
3

Sugerowałbym rozszerzenie TextView i zawsze używanie niestandardowego TextView w układach XML lub wszędzie tam, gdzie potrzebujesz TextView. W niestandardowym widoku tekstowym zastąpsetTypeface

@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
źródło
2

Rozwiązanie Toma działa świetnie, ale działa tylko z TextView i EditText.

Jeśli chcesz pokryć większość widoków (RadioGroup, TextView, Checkbox ...), stworzyłem metodę, która to robi:

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

Ta metoda przywróci styl widoku ustawiony w XML (pogrubienie, kursywa ...) i zastosuje je, jeśli takie istnieją.

W przypadku ListView zawsze tworzę adapter i ustawiam czcionkę w getView.

FR073N
źródło
2

Napisałem klasę przypisującą krój pisma do widoków w bieżącej hierarchii widoków i opierając się na bieżących właściwościach kroju pisma (pogrubienie, normalne, jeśli chcesz, możesz dodać inne style):

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

Teraz we wszystkich fragmentach w onViewCreated lub onCreateView, we wszystkich działaniach w onCreate i we wszystkich adapterach widoku w getView lub newView wystarczy wywołać:

typefaceAssigner.assignTypeface(view);
Iwan Krawczenko
źródło
2

w API 26 z build.gradle 3.0.0 i nowszym możesz utworzyć katalog czcionek w res i użyć tej linii w swoim stylu

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

do zmiany build.gradle użyj tego w zależnościach build.gradle

classpath 'com.android.tools.build:gradle:3.0.0'
Mohammad Hosein Heidari
źródło
i jak wstawić czcionkę do projektu?
azerafati,
@azerafati skopiuj swoją czcionkę do res / font
Mohammad Hosein Heidari
tak, tanx. Już to dostałem. Ale itemto nie ustawia czcionki dla całej aplikacji. jakaś wskazówka?
azerafati,
1

Wreszcie Google zdało sobie sprawę z powagi tego problemu (stosując niestandardową czcionkę do składników interfejsu użytkownika) i opracowało dla niego czyste rozwiązanie.

Najpierw musisz zaktualizować, aby obsługiwał bibliotekę 26+ (może być także konieczna aktualizacja klasy {4.0+}, studio Android), a następnie możesz utworzyć nowy folder zasobów o nazwie font. W tym folderze możesz umieścić zasoby czcionek (.tff, ...). Następnie musisz zastąpić domyślną aplikację i nałożyć na nią niestandardową czcionkę :)

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

Uwaga: jeśli chcesz obsługiwać urządzenia ze starszym API niż 16, musisz użyć przestrzeni nazw aplikacji zamiast Androida!

Mr.Q
źródło
0

Chciałbym również poprawić odpowiedź Westona na API 21 Android 5.0.

Miałem ten sam problem na moim telefonie Samsung s5, gdy korzystałem z czcionki DOMYŚLNEJ. (z innymi czcionkami działa dobrze)

Udało mi się go uruchomić, ustawiając krój pisma (na przykład „sans”) w plikach XML dla każdego widoku tekstu lub przycisku

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

oraz w MyApplication Class:

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

Mam nadzieję, że to pomoże.

Stevenwood
źródło
0

Kaligrafia działa całkiem dobrze, ale nie jest dla mnie odpowiednia, ponieważ nie obsługuje różnych grubości (pogrubienie, kursywa itp.) Dla rodziny czcionek.

Więc spróbowałem Fontaina , która pozwala zdefiniować niestandardowe widoki i zastosować je do niestandardowych rodzin czcionek.

w celu korzystania z Fontain, należy dodać do build.gradle modułu aplikacji:

compile 'com.scopely:fontain:1.0.0'

Następnie zamiast zwykłego TextView powinieneś użyć FontTextView

Przykład FontTextView z dużymi i pogrubionymi treściami:

 <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
źródło
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);

    }

}
Akademia EasyLearn
źródło
Nie jest to idealne rozwiązanie. Stosując to rozwiązanie będzie wymagało niestandardowych obejścia domyślny dostęp do Android TextViews
blueware
0

Aby zmienić domyślną rodzinę czcionek TextViews, zastąp tekst textViewStyle w motywie aplikacji.

Aby użyć niestandardowej czcionki w fontFamily, użyj zasobów czcionek znajdujących się w bibliotece obsługi.

Ta funkcja została dodana w Androidzie 26, ale przeniesiona do starszych wersji za pośrednictwem 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
źródło
0

Od wydania Androida Oreo i jego biblioteki wsparcia (26.0.0) możesz to zrobić łatwo. Odnieś się do tej odpowiedzi w innym pytaniu.

Zasadniczo twój ostateczny styl będzie wyglądał następująco:

<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
źródło
-15

Tak, możliwe jest ustawienie czcionki dla całej aplikacji.

Najłatwiejszym sposobem na osiągnięcie tego jest spakowanie żądanej czcionki (czcionek) w aplikacji.

Aby to zrobić, po prostu utwórz zasób / folder w katalogu głównym projektu i umieść czcionki (w formacie TrueType lub TTF) w zasobach.

Możesz na przykład utworzyć zasoby / czcionki / i umieścić tam swoje pliki 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
źródło