Używanie niestandardowego kroju pisma w systemie Android

111

Chcę użyć niestandardowej czcionki dla mojej aplikacji na Androida, którą tworzę.
Mogę indywidualnie zmienić krój każdego obiektu z Code, ale mam ich setki.

Więc,

  • Czy istnieje sposób, aby to zrobić z poziomu XML? [Ustawianie niestandardowego kroju pisma]
  • Czy jest sposób na zrobienie tego z kodu w jednym miejscu, aby powiedzieć, że cała aplikacja i wszystkie komponenty powinny używać niestandardowego kroju pisma zamiast domyślnego?
Codevalley
źródło
stackoverflow.com/questions/5541058/... Spójrz na tym poście napisałem kompletny kod tutaj dotyczące tego problemu
Manish Singla
Możesz użyć zmiennych statycznych w swojej głównej aktywności, aby przechowywać odniesienia do osadzonych czcionek. To spowodowałoby, że istniałby jeden trwały zestaw czcionek, który nie zostałby pobrany przez GC.
Jacksonkr
Metoda fabryczna. Lub metoda, która pozwala zobaczyć i ustawić wszystkie ustawienia czcionek i kroju pisma.
mtmurdock
Nowa biblioteka obsługi 26 umożliwia teraz używanie czcionek w XML. Oto jak to zrobić Czcionki w XML
Vinicius Silva

Odpowiedzi:

80

Czy istnieje sposób, aby to zrobić z poziomu XML?

Nie, przepraszam. Możesz określić wbudowane kroje pisma tylko za pomocą XML.

Czy jest sposób na zrobienie tego z kodu w jednym miejscu, aby powiedzieć, że cała aplikacja i wszystkie komponenty powinny używać niestandardowego kroju pisma zamiast domyślnego?

Nie żebym był tego świadomy.

Obecnie istnieje wiele różnych opcji:

  • Zasoby czcionek i backporty w zestawie SDK systemu Android, jeśli używasz appcompat

  • Biblioteki innych firm dla tych, którzy nie używają appcompat, choć nie wszystkie będą obsługiwać definiowanie czcionki w zasobach układu

CommonsWare
źródło
4
@Codevalley: Cóż, pod wieloma względami lepszą odpowiedzią jest nie zawracać sobie głowy niestandardowymi czcionkami. Zwykle są duże, co zwiększa liczbę pobieranych plików i zmniejsza liczbę osób, które będą pobierać i nadal używać Twojej aplikacji.
CommonsWare
22
@CommonsWare: Niekoniecznie się zgadzam. Krój pisma jest integralną częścią stylizacji interfejsu użytkownika, podobnie jak w przypadku obrazów tła itp. Rozmiar nie zawsze jest duży. Na przykład czcionka w moim przypadku ma tylko 19,6 KB :)
Codevalley
2
@Amit: Nie było żadnych zmian w tej kwestii. Istnieje wiele fragmentów kodu, które pomagają uprościć stosowanie kroju pisma do interfejsu użytkownika w Javie, ale nie ma sposobu, aby to zrobić z poziomu XML.
CommonsWare
1
@Amit: „Ale czy to oznacza, że ​​będę musiał utworzyć własne pliki .ttf lub .otf dla niestandardowych czcionek?” -- yyy ... nie. Możesz użyć istniejących czcionek TTF / OTF, chociaż nie wszystkie mogą działać. Problem w tym pytaniu polega na tym, jak zastosować te czcionki w całej aplikacji, co nadal nie jest możliwe, i właśnie o tym mówiłem w moim komentarzu.
CommonsWare
1
Ta zaakceptowana odpowiedź jest przestarzała i byłoby wspaniale ją zaktualizować lub usunąć.
Sky Kelsey,
109

Tak to mozliwe.

Musisz stworzyć własny widok, który rozszerzy widok tekstu.

W attrs.xmlw valuesfolderze:

<resources>
    <declare-styleable name="MyTextView">
        <attr name="first_name" format="string"/>
        <attr name="last_name" format="string"/>
        <attr name="ttf_name" format="string"/>
    </declare-styleable>
</resources>

W main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lht="http://schemas.android.com/apk/res/com.lht"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView  android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="Hello"/>
    <com.lht.ui.MyTextView  
        android:id="@+id/MyTextView"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content"
        android:text="Hello friends"
        lht:ttf_name="ITCBLKAD.TTF"
        />   
</LinearLayout>

W MyTextView.java:

package com.lht.ui;

import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;

public class MyTextView extends TextView {

    Context context;
    String ttfName;

    String TAG = getClass().getName();

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;

        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            Log.i(TAG, attrs.getAttributeName(i));
            /*
             * Read value of custom attributes
             */

            this.ttfName = attrs.getAttributeValue(
                    "http://schemas.android.com/apk/res/com.lht", "ttf_name");
            Log.i(TAG, "firstText " + firstText);
            // Log.i(TAG, "lastText "+ lastText);

            init();
        }

    }

    private void init() {
        Typeface font = Typeface.createFromAsset(context.getAssets(), ttfName);
        setTypeface(font);
    }

    @Override
    public void setTypeface(Typeface tf) {

        // TODO Auto-generated method stub
        super.setTypeface(tf);
    }

}
Manish Singla
źródło
4
To zadziała, ale tylko w przypadku TextView. Będzie wymagać niestandardowych podklas dla każdej klasy widżetu, która dziedziczy z TextViewmiejsca, w którym ta sama zdolność jest pożądana.
CommonsWare,
Cześć Manish, Dzięki za rozwiązanie. Chociaż mam problem z używaniem tego. Wciąż otrzymuję „nie znaleziono identyfikator zasobu dla atrybutu ttf_name w opakowaniu com.lht.android”
Kshitij Aggarwal
17
To zadziała, ale przed ICS przydzieli pamięć dla czcionek dla każdego utworzonego widoku: code.google.com/p/android/issues/detail?id=9904 Sposobem na rozwiązanie tego problemu jest utworzenie globalnie dostępnego statyczna mapa wszystkich czcionek
utworzonych
Po co nam pętla? Czy nie powinna działać z pojedynczym wywołaniem?
Guillermo Tobar
1
@AlaksiejN. na wypadek, gdybyś musiał ustawić różne kroje pisma na różne widoki tekstowe ...
Nick
49

Zrobiłem to w bardziej „brutalny sposób”, który nie wymaga zmian w układzie XML ani w Aktywnościach.

Testowano na Androidzie w wersji od 2.1 do 4.4. Uruchom to podczas uruchamiania aplikacji w klasie Application:

private void setDefaultFont() {

    try {
        final Typeface bold = Typeface.createFromAsset(getAssets(), DEFAULT_BOLD_FONT_FILENAME);
        final Typeface italic = Typeface.createFromAsset(getAssets(), DEFAULT_ITALIC_FONT_FILENAME);
        final Typeface boldItalic = Typeface.createFromAsset(getAssets(), DEFAULT_BOLD_ITALIC_FONT_FILENAME);
        final Typeface regular = Typeface.createFromAsset(getAssets(),DEFAULT_NORMAL_FONT_FILENAME);

        Field DEFAULT = Typeface.class.getDeclaredField("DEFAULT");
        DEFAULT.setAccessible(true);
        DEFAULT.set(null, regular);

        Field DEFAULT_BOLD = Typeface.class.getDeclaredField("DEFAULT_BOLD");
        DEFAULT_BOLD.setAccessible(true);
        DEFAULT_BOLD.set(null, bold);

        Field sDefaults = Typeface.class.getDeclaredField("sDefaults");
        sDefaults.setAccessible(true);
        sDefaults.set(null, new Typeface[]{
                regular, bold, italic, boldItalic
        });

    } catch (NoSuchFieldException e) {
        logFontError(e);
    } catch (IllegalAccessException e) {
        logFontError(e);
    } catch (Throwable e) {
        //cannot crash app if there is a failure with overriding the default font!
        logFontError(e);
    }
}

Bardziej kompletny przykład można znaleźć pod adresem http://github.com/perchrh/FontOverrideExample

Per Christian Henden
źródło
To dla mnie najlepsze rozwiązanie.
Igor K
2
domyślnie nie działa dla mnie. jeśli używam stałej codeszerokości, a następnie ustawię <style name = "AppTheme" parent = "AppBaseTheme"> <item name = "android: typeface"> monospace </item> </style>, codeto działa, ale nie dla pogrubienia. Dodałem ten kod w klasie, która rozszerza Application. czy to właściwe miejsce? @ P-chan
Christopher Rivera
2
@ChristopherRivera Tak, dodaj go do klasy Application swojej aplikacji, upewnij się, że działa onCreate. Rzut oka na grepcode.com/file/repository.grepcode.com/java/ext/… Sugerowałbym zastąpienie dodatkowego pola dla monospace, a także tego w moim przykładowym kodzie powyżej!
Per Christian Henden,
2
Dobra, zmieniłem na pole SERIF i zadziałało :)
Abdullah Umer
3
Ta odpowiedź odnosi się do tej odpowiedzi i zapewnia czystsze podejście imho.
adamdport
36

Chociaż popieram odpowiedź Manisha jako najszybszą i najbardziej ukierunkowaną metodę, widziałem również naiwne rozwiązania, które po prostu rekurencyjnie powtarzają hierarchię widoków i aktualizują po kolei kroje wszystkich elementów. Coś takiego:

public static void applyFonts(final View v, Typeface fontToSet)
{
    try {
        if (v instanceof ViewGroup) {
            ViewGroup vg = (ViewGroup) v;
            for (int i = 0; i < vg.getChildCount(); i++) {
                View child = vg.getChildAt(i);
                applyFonts(child, fontToSet);
            }
        } else if (v instanceof TextView) {
            ((TextView)v).setTypeface(fontToSet);
        }
    } catch (Exception e) {
        e.printStackTrace();
        // ignore
    }
}

Musisz wywołać tę funkcję w swoich widokach zarówno po zawyżeniu układu, jak iw onContentChanged()metodach działania.

pospi
źródło
Dość dużo. W tamtym czasie był to jedyny sposób na zrobienie tego w ramach czasowych projektu. W razie potrzeby przydatny skrót (:
pospi
23

Udało mi się to zrobić w sposób scentralizowany, oto wynik:

wprowadź opis obrazu tutaj

Mam następujące Activityi rozszerzam je, jeśli potrzebuję niestandardowych czcionek:

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater.Factory;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class CustomFontActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    getLayoutInflater().setFactory(new Factory() {

        @Override
        public View onCreateView(String name, Context context,
                AttributeSet attrs) {
            View v = tryInflate(name, context, attrs);
            if (v instanceof TextView) {
                setTypeFace((TextView) v);
            }
            return v;
        }
    });
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

private View tryInflate(String name, Context context, AttributeSet attrs) {
    LayoutInflater li = LayoutInflater.from(context);
    View v = null;
    try {
        v = li.createView(name, null, attrs); 
    } catch (Exception e) {
        try {
            v = li.createView("android.widget." + name, null, attrs);
        } catch (Exception e1) {
        }
    }
    return v;
}

private void setTypeFace(TextView tv) {
    tv.setTypeface(FontUtils.getFonts(this, "MTCORSVA.TTF"));
}
}

Ale jeśli korzystam z aktywności z pakietu wsparcia, FragmentActivityto używam tego Activity:

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class CustomFontFragmentActivity extends FragmentActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

// we can't setLayout Factory as its already set by FragmentActivity so we
// use this approach
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    View v = super.onCreateView(name, context, attrs);
    if (v == null) {
        v = tryInflate(name, context, attrs);
        if (v instanceof TextView) {
            setTypeFace((TextView) v);
        }
    }
    return v;
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View onCreateView(View parent, String name, Context context,
        AttributeSet attrs) {
    View v = super.onCreateView(parent, name, context, attrs);
    if (v == null) {
        v = tryInflate(name, context, attrs);
        if (v instanceof TextView) {
            setTypeFace((TextView) v);
        }
    }
    return v;
}

private View tryInflate(String name, Context context, AttributeSet attrs) {
    LayoutInflater li = LayoutInflater.from(context);
    View v = null;
    try {
        v = li.createView(name, null, attrs);
    } catch (Exception e) {
        try {
            v = li.createView("android.widget." + name, null, attrs);
        } catch (Exception e1) {
        }
    }
    return v;
}

private void setTypeFace(TextView tv) {
    tv.setTypeface(FontUtils.getFonts(this, "MTCORSVA.TTF"));
}
}

Nie testowałem jeszcze tego kodu z Fragments, ale mam nadzieję, że zadziała.

My FontUtilsis simple, które rozwiązuje również wspomniany tutaj problem z pre-ICS https://code.google.com/p/android/issues/detail?id=9904 :

import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.graphics.Typeface;

public class FontUtils {

private static Map<String, Typeface> TYPEFACE = new HashMap<String, Typeface>();

public static Typeface getFonts(Context context, String name) { 
    Typeface typeface = TYPEFACE.get(name);
    if (typeface == null) {
        typeface = Typeface.createFromAsset(context.getAssets(), "fonts/"
                + name);
        TYPEFACE.put(name, typeface);
    }
    return typeface;
}
}
M-WaJeEh
źródło
2
Powinno to uzyskać więcej głosów. Działa i ma zrzut ekranu do demonstracji
ericn
10

Hej, potrzebuję też 2 różnych czcionek w mojej aplikacji dla różnych widgów! Używam w ten sposób:

W mojej klasie Application tworzę metodę statyczną:

public static Typeface getTypeface(Context context, String typeface) {
    if (mFont == null) {
        mFont = Typeface.createFromAsset(context.getAssets(), typeface);
    }
    return mFont;
}

Krój pisma String reprezentuje xyz.ttf w folderze zasobów. (utworzyłem klasę stałych) Teraz możesz jej używać wszędzie w swojej aplikacji:

mTextView = (TextView) findViewById(R.id.text_view);
mTextView.setTypeface(MyApplication.getTypeface(this, Constants.TYPEFACE_XY));

Jedynym problemem jest to, że potrzebujesz tego dla każdego widżetu, w którym chcesz używać czcionki! Ale myślę, że to najlepszy sposób.

Informatic0re
źródło
4

Korzystając z sugestii pospi i pracując z właściwością „tag”, tak jak zrobił to Richard , stworzyłem niestandardową klasę, która ładuje moje niestandardowe czcionki i stosuje je do widoków zgodnie z ich tagami.

Tak więc zasadniczo zamiast ustawiania TypeFace w atrybucie android: fontFamily używasz android: tag attritube i ustawiasz go na jedną ze zdefiniowanych wyliczeń.

public class Fonts {
    private AssetManager mngr;

    public Fonts(Context context) {
        mngr = context.getAssets();
    }
    private enum AssetTypefaces {
        RobotoLight,
        RobotoThin,
        RobotoCondensedBold,
        RobotoCondensedLight,
        RobotoCondensedRegular
    }

    private Typeface getTypeface(AssetTypefaces font) {
        Typeface tf = null;
        switch (font) {
            case RobotoLight:
                tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Light.ttf");
                break;
            case RobotoThin:
                tf = Typeface.createFromAsset(mngr,"fonts/Roboto-Thin.ttf");
                break;
            case RobotoCondensedBold:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Bold.ttf");
                break;
            case RobotoCondensedLight:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Light.ttf");
                break;
            case RobotoCondensedRegular:
                tf = Typeface.createFromAsset(mngr,"fonts/RobotoCondensed-Regular.ttf");
                break;
            default:
                tf = Typeface.DEFAULT;
                break;
        }
        return tf;
    }
    public void setupLayoutTypefaces(View v) {
        try {
            if (v instanceof ViewGroup) {
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++) {
                    View child = vg.getChildAt(i);
                    setupLayoutTypefaces(child);
                }
            } else if (v instanceof TextView) {
                if (v.getTag().toString().equals(AssetTypefaces.RobotoLight.toString())){
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoLight));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedRegular.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedRegular));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedBold.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedBold));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoCondensedLight.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoCondensedLight));
                }else if (v.getTag().toString().equals(AssetTypefaces.RobotoThin.toString())) {
                    ((TextView)v).setTypeface(getTypeface(AssetTypefaces.RobotoThin));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            // ignore
        }
    }
}

W swojej Aktywności lub Fragmentie po prostu dzwonisz

Fonts fonts = new Fonts(getActivity());
fonts.setupLayoutTypefaces(mainLayout);
majinboo
źródło
4

Znalazłem fajne rozwiązanie na blogu Lisy Wray . Dzięki nowemu powiązaniu danych możliwe jest ustawienie czcionki w plikach XML.

@BindingAdapter({"bind:font"})
public static void setFont(TextView textView, String fontName){
    textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));
}

W XML:

<TextView
app:font="@{`Source-Sans-Pro-Regular.ttf`}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
userM1433372
źródło
Czy możesz zasugerować przykład za pomocą powiązania danych? Byłoby to bardzo mile widziane.
Sri Krishna
3

Myślę, że można to zrobić w wygodniejszy sposób. Następująca klasa ustawi niestandardowy krój czcionki dla wszystkich składników aplikacji (z ustawieniem na klasę).

/**
 * Base Activity of our app hierarchy.
 * @author SNI
 */
public class BaseActivity extends Activity {

    private static final String FONT_LOG_CAT_TAG = "FONT";
    private static final boolean ENABLE_FONT_LOGGING = false;

    private Typeface helloTypeface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        helloTypeface = Typeface.createFromAsset(getAssets(), "fonts/<your type face in assets/fonts folder>.ttf");
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        View view = super.onCreateView(name, context, attrs);
        return setCustomTypeFaceIfNeeded(name, attrs, view);
    }

    @Override
    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        View view = super.onCreateView(parent, name, context, attrs);
        return setCustomTypeFaceIfNeeded(name, attrs, view);
    }

    protected View setCustomTypeFaceIfNeeded(String name, AttributeSet attrs, View view) {
        View result = null;
        if ("TextView".equals(name)) {
            result = new TextView(this, attrs);
            ((TextView) result).setTypeface(helloTypeface);
        }

        if ("EditText".equals(name)) {
            result = new EditText(this, attrs);
            ((EditText) result).setTypeface(helloTypeface);
        }

        if ("Button".equals(name)) {
            result = new Button(this, attrs);
            ((Button) result).setTypeface(helloTypeface);
        }

        if (result == null) {
            return view;
        } else {
            if (ENABLE_FONT_LOGGING) {
                Log.v(FONT_LOG_CAT_TAG, "A type face was set on " + result.getId());
            }
            return result;
        }
    }

}
Snicolas
źródło
2

Domyślne implementacje LayoutInflater nie obsługują określania kroju czcionki z xml. Widziałem jednak, jak to się robi w XML, dostarczając niestandardową fabrykę dla LayoutInflater, która analizuje takie atrybuty ze znacznika xml.

Podstawowa struktura by tak wyglądała.

public class TypefaceInflaterFactory implements LayoutInflater.Factory {

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        // CUSTOM CODE TO CREATE VIEW WITH TYPEFACE HERE
        // RETURNING NULL HERE WILL TELL THE INFLATER TO USE THE
        // DEFAULT MECHANISMS FOR INFLATING THE VIEW FROM THE XML
    }

}

public class BaseActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LayoutInflater.from(this).setFactory(new TypefaceInflaterFactory());
    }
}

Ten artykuł zawiera bardziej szczegółowe wyjaśnienie tych mechanizmów i sposobu, w jaki autor próbuje w ten sposób zapewnić obsługę układu XML dla krojów pisma. Kod autorskiej implementacji można znaleźć tutaj .

Ryan Thomas
źródło
1

Ustawianie niestandardowej czcionki na zwykły ProgressDialog / AlertDialog:

font=Typeface.createFromAsset(getAssets(),"DroidSans.ttf");

ProgressDialog dialog = ProgressDialog.show(this, "titleText", "messageText", true);
((TextView)dialog.findViewById(Resources.getSystem().getIdentifier("message", "id", "android"))).setTypeface(font);
((TextView)dialog.findViewById(Resources.getSystem().getIdentifier("alertTitle", "id", "android"))).setTypeface(font);
artProc
źródło
1

Tak, jest to możliwe poprzez zastąpienie domyślnego kroju pisma. I po to rozwiązanie i to działało jak czar dla wszystkich tekstów TextView i ActionBar też z jedną zmianę.

public class MyApp extends Application {

  @Override
  public void onCreate() {
    TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/Roboto-Regular.ttf"); // font from assets: "assets/fonts/Roboto-Regular.ttf
  }
}

style.xml

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/pantone</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
    <item name="android:windowDisablePreview">true</item>
    <item name="android:typeface">serif</item>
</style>

Zamiast pliku themes.xml, jak wspomniano w powyższym linku, wspomniałem o domyślnej czcionce do zastąpienia w moim styles.xml w moim domyślnym tagu motywu aplikacji. Domyślne kroje pisma, które można nadpisać, to serif, sans, monospace i normal.

TypefaceUtil.java

public class TypefaceUtil {

    /**
     * Using reflection to override default typeface
     * NOTICE: DO NOT FORGET TO SET TYPEFACE FOR APP THEME AS DEFAULT TYPEFACE WHICH WILL BE OVERRIDDEN
     * @param context to work with assets
     * @param defaultFontNameToOverride for example "monospace"
     * @param customFontFileNameInAssets file name of the font from assets
     */
    public static void overrideFont(Context context, String defaultFontNameToOverride, String customFontFileNameInAssets) {
        try {
            final Typeface customFontTypeface = Typeface.createFromAsset(context.getAssets(), customFontFileNameInAssets);

            final Field defaultFontTypefaceField = Typeface.class.getDeclaredField(defaultFontNameToOverride);
            defaultFontTypefaceField.setAccessible(true);
            defaultFontTypefaceField.set(null, customFontTypeface);
        } catch (Exception e) {
            Log.e("Can not set custom font " + customFontFileNameInAssets + " instead of " + defaultFontNameToOverride);
        }
    }
}

Początkowo nie wiedziałem, że kroje pisma do nadpisania są stałe i zestaw zdefiniowanych wartości, ale ostatecznie pomogło mi to zrozumieć, jak Android radzi sobie z czcionkami i krojami pisma oraz ich domyślnymi wartościami, co jest oczywiście innym punktem.

Amruta-Pani
źródło
0

Nie wiem, czy zmienia to całą aplikację, ale udało mi się zmienić niektóre komponenty, których nie można by zmienić w inny sposób:

Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Lucida Sans Unicode.ttf");
Typeface.class.getField("DEFAULT").setAccessible(true);
Typeface.class.getField("DEFAULT_BOLD").setAccessible(true);
Typeface.class.getField("DEFAULT").set(null, tf);
Typeface.class.getField("DEFAULT_BOLD").set(null, tf);
Richard
źródło
2
Niestety to już nie działa: java.lang.IllegalAccessException: pole jest oznaczone jako „końcowe” w java.lang.reflect.Field.setField (metoda natywna) w java.lang.reflect.Field.set (Field.java: 556)
BoD
0

Podoba mi się sugestia pospi. Dlaczego nie pójść na całość, użyj właściwości „tag” widoku (którą możesz określić w XML - „android: tag”), aby określić dodatkowe style, których nie możesz wykonać w XML. Lubię JSON, więc użyłbym ciągu JSON do określenia zestawu klucz / wartość. Ta klasa działa - po prostu zadzwoń Style.setContentView(this, [resource id])do swoich zajęć.

public class Style {

  /**
   * Style a single view.
   */
  public static void apply(View v) {
    if (v.getTag() != null) {
      try {
        JSONObject json = new JSONObject((String)v.getTag());
        if (json.has("typeface") && v instanceof TextView) {
          ((TextView)v).setTypeface(Typeface.createFromAsset(v.getContext().getAssets(),
                                                             json.getString("typeface")));
        }
      }
      catch (JSONException e) {
        // Some views have a tag without it being explicitly set!
      }
    }
  }

  /**
   * Style the passed view hierarchy.
   */
  public static View applyTree(View v) {
    apply(v);
    if (v instanceof ViewGroup) {
      ViewGroup g = (ViewGroup)v;
      for (int i = 0; i < g.getChildCount(); i++) {
        applyTree(g.getChildAt(i));
      }
    }
    return v;
  }

  /**
   * Inflate, style, and set the content view for the passed activity.
   */
  public static void setContentView(Activity activity, int resource) {
    activity.setContentView(applyTree(activity.getLayoutInflater().inflate(resource, null)));
  }
}

Oczywiście chciałbyś zająć się czymś więcej niż tylko krojem czcionki, aby korzystanie z JSON było opłacalne.

Zaletą właściwości „tag” jest to, że możesz ustawić ją na podstawowym stylu, którego używasz jako motywu, dzięki czemu będzie ona automatycznie stosowana do wszystkich widoków. EDYCJA: Wykonanie tej czynności powoduje awarię podczas inflacji na Androidzie 4.0.3. Nadal można używać stylu i indywidualnie stosować go do widoków tekstu.

Jedna rzecz, którą zobaczysz w kodzie - niektóre widoki mają tag, ale żaden nie jest wyraźnie ustawiony - co dziwne, jest to ciąg znaków „Αποκοπή” - który według tłumacza Google jest „wycięty” po grecku! Co do cholery...?

Richard
źródło
0

Odpowiedź @ majinboo została poprawiona pod kątem wydajności i zarządzania pamięcią. Dowolne działanie powiązane z więcej niż jedną czcionką może używać tej klasy Font, podając sam konstruktor jako parametr.

@Override
public void onCreate(Bundle savedInstanceState)
{
    Font font = new Font(this);
}

Klasa poprawionych czcionek jest następująca:

public class Fonts
{
    private HashMap<AssetTypefaces, Typeface> hashMapFonts;

    private enum AssetTypefaces
    {
        RobotoLight,
        RobotoThin,
        RobotoCondensedBold,
        RobotoCondensedLight,
        RobotoCondensedRegular
    }

    public Fonts(Context context)
    {
        AssetManager mngr = context.getAssets();

        hashMapFonts = new HashMap<AssetTypefaces, Typeface>();
        hashMapFonts.put(AssetTypefaces.RobotoLight, Typeface.createFromAsset(mngr, "fonts/Roboto-Light.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoThin, Typeface.createFromAsset(mngr, "fonts/Roboto-Thin.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedBold, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Bold.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedLight, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Light.ttf"));
        hashMapFonts.put(AssetTypefaces.RobotoCondensedRegular, Typeface.createFromAsset(mngr, "fonts/RobotoCondensed-Regular.ttf"));
    }

    private Typeface getTypeface(String fontName)
    {
        try
        {
            AssetTypefaces typeface = AssetTypefaces.valueOf(fontName);
            return hashMapFonts.get(typeface);
        }
        catch (IllegalArgumentException e)
        {
            // e.printStackTrace();
            return Typeface.DEFAULT;
        }
    }

    public void setupLayoutTypefaces(View v)
    {
        try
        {
            if (v instanceof ViewGroup)
            {
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++)
                {
                    View child = vg.getChildAt(i);
                    setupLayoutTypefaces(child);
                }
            }
            else if (v instanceof TextView)
            {
                ((TextView) v).setTypeface(getTypeface(v.getTag().toString()));
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            // ignore
        }
    }
}
Suphi ÇEVİKER
źródło
0

Praca dla platformy 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);
        }
    }
}

Wdrażanie 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
0

Wygląda na to, że korzystanie z niestandardowych czcionek zostało ułatwione dzięki Androidowi O. Aby to osiągnąć, możesz w zasadzie użyć XML. Dołączyłem link do oficjalnej dokumentacji Androida w celach informacyjnych i mam nadzieję, że pomoże to ludziom, którzy nadal potrzebują tego rozwiązania. Praca z niestandardowymi czcionkami w systemie Android

Mohale
źródło
0

Warto wiedzieć, że począwszy od systemu Android 8.0 (poziom interfejsu API 26) można użyć niestandardowej czcionki w języku XML .

Mówiąc najprościej, możesz to zrobić w następujący sposób.

  1. Umieść czcionkę w folderze res/font.

  2. Użyj go w atrybucie widgetu

<Button android:fontFamily="@font/myfont"/>

lub włóż res/values/styles.xml

<style name="MyButton" parent="android:Widget.Button">
    <item name="android:fontFamily">@font/myfont</item>
</style>

i użyj go jako stylu

<Button style="@style/MyButton"/>
Vic
źródło
0

Użyj atrybutu „fontPath” bezpośrednio w pliku xml.

do użytku w style.xml

<item name = "fontPath"> fonts / ProximaNovaSemibold.ttf </item>

do użytku w pliku układu bezpośredniego

fontPath = "fonts / ProximaNovaBold.ttf"

(Uwaga: nie ma potrzeby używania atrybutu app / android w prefiksie)

Dharmendra
źródło
-6

Absolutnie możliwe. Można to zrobić na wiele sposobów. Najszybciej stwórz warunek metodą try - catch .. wypróbuj warunek określonego stylu czcionki, wyłap błąd i zdefiniuj inny styl czcionki.

haloyudi
źródło
7
czy możesz dostarczyć demo, aby udowodnić swoją odpowiedź?
Mark