Verwenden von benutzerdefinierten Schriftarten in Android TextView mit XML

92

Ich habe meinem Ordner "Assets / Fonts" eine benutzerdefinierte Schriftartdatei hinzugefügt. Wie verwende ich es aus meinem XML?

Ich kann es aus dem Code wie folgt verwenden:

TextView text = (TextView) findViewById(R.id.textview03);
Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/Molot.otf");
text.setTypeface(tf);

Kann ich es nicht mit einem android:typeface="/fonts/Molot.otf"Attribut aus XML machen ?

Nilanchal
quelle
Ich habe viel danach gesucht und es gibt keine Möglichkeit, dies mit XML zu tun.
CD
2
Versuchen Sie, diesen Beitrag zu überprüfen stackoverflow.com/questions/2376250/…
dor506
Schauen Sie sich diese Antwort an ! Sie können mehrere Schriftarten und XML verwenden.
Rafa0809
Wie bereits erwähnt, können Sie Kalligraphie verwenden , um dies zu erreichen.
mbonnin
Überprüfen Sie diesen Artikel gadgetsaint.com/tips/…
ASP

Antworten:

45

Kurze Antwort: Nein. Android bietet keine integrierte Unterstützung für das Anwenden benutzerdefinierter Schriftarten auf Text-Widgets über XML.

Es gibt jedoch eine Problemumgehung, die nicht besonders schwer zu implementieren ist.

Zuerst

Sie müssen Ihr eigenes Stylable definieren. Öffnen / erstellen Sie in Ihrem Ordner / res / values ​​die Datei attrs.xml und fügen Sie ein deklarierbares Objekt wie folgt hinzu:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FontText">
        <attr name="typefaceAsset" format="string"/>
    </declare-styleable>
</resources>

Zweite

Angenommen, Sie möchten dieses Widget häufig verwenden, sollten Sie einen einfachen Cache für die geladenen TypefaceObjekte einrichten , da das Laden aus dem Speicher im laufenden Betrieb einige Zeit in Anspruch nehmen kann. Etwas wie:

public class FontManager {
    private static FontManager instance;

    private AssetManager mgr;

    private Map<String, Typeface> fonts;

    private FontManager(AssetManager _mgr) {
        mgr = _mgr;
        fonts = new HashMap<String, Typeface>();
    }

    public static void init(AssetManager mgr) {
        instance = new FontManager(mgr);
    }

    public static FontManager getInstance() {
        if (instance == null) {
            // App.getContext() is just one way to get a Context here
            // getContext() is just a method in an Application subclass
            // that returns the application context
            AssetManager assetManager = App.getContext().getAssets();
            init(assetManager);
        }
        return instance;
    }

    public Typeface getFont(String asset) {
        if (fonts.containsKey(asset))
            return fonts.get(asset);

        Typeface font = null;

        try {
            font = Typeface.createFromAsset(mgr, asset);
            fonts.put(asset, font);
        } catch (Exception e) {

        }

        if (font == null) {
            try {
                String fixedAsset = fixAssetFilename(asset);
                font = Typeface.createFromAsset(mgr, fixedAsset);
                fonts.put(asset, font);
                fonts.put(fixedAsset, font);
            } catch (Exception e) {

            }
        }

        return font;
    }

    private String fixAssetFilename(String asset) {
        // Empty font filename?
        // Just return it. We can't help.
        if (TextUtils.isEmpty(asset))
            return asset;

        // Make sure that the font ends in '.ttf' or '.ttc'
        if ((!asset.endsWith(".ttf")) && (!asset.endsWith(".ttc")))
            asset = String.format("%s.ttf", asset);

        return asset;
    }
}

In diesem Fall können Sie .ttc-Dateierweiterungen verwenden, diese sind jedoch nicht getestet.

Dritte

Erstellen Sie eine neue Klasse, die Unterklassen enthält TextView. Dieses spezielle Beispiel berücksichtigt die definierte XML - Schrift ( bold, italicusw.) und es auf die Schriftart (vorausgesetzt , Sie eine .ttc Datei verwenden).

/**
 * TextView subclass which allows the user to define a truetype font file to use as the view's typeface.
 */
public class FontText extends TextView {
    public FontText(Context context) {
        this(context, null);
    }

    public FontText(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FontText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        if (isInEditMode())
            return;

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FontText);

        if (ta != null) {
            String fontAsset = ta.getString(R.styleable.FontText_typefaceAsset);

            if (!TextUtils.isEmpty(fontAsset)) {
                Typeface tf = FontManager.getInstance().getFont(fontAsset);
                int style = Typeface.NORMAL;
                float size = getTextSize();

                if (getTypeface() != null)
                    style = getTypeface().getStyle();

                if (tf != null)
                    setTypeface(tf, style);
                else
                    Log.d("FontText", String.format("Could not create a font from asset: %s", fontAsset));
            }
        }
    }
}

Schließlich

Ersetzen Sie die Instanzen TextViewin Ihrem XML durch den vollständig qualifizierten Klassennamen. Deklarieren Sie Ihren benutzerdefinierten Namespace wie den Android-Namespace. Beachten Sie, dass das "typefaceAsset" auf eine .ttf- oder .ttc-Datei verweisen sollte, die in Ihrem Verzeichnis / assets enthalten ist.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.FontText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is a custom font text"
        custom:typefaceAsset="fonts/AvenirNext-Regular.ttf"/>
</RelativeLayout>
themarshal
quelle
Gute Antwort! Eine Frage: Warum sind Sie zurückgekehrt, wenn isInEditMode?
Avi Shukron
04-11 18: 18: 32.685 3506-3506 / com.example.demo E / AndroidRuntime ﹕ FATAL EXCEPTION: Hauptprozess: com.example.demo, PID: 3506 android.view.InflateException: Binäre XML-Dateizeile 2: Fehler Aufblasen der Klasse com.example.demo.libraries.LatoTextView unter android.view.LayoutInflater.createView (LayoutInflater.java:620) unter android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:696) unter android.view.LayoutInflater. LayoutInflater.java:469) bei android.view.LayoutInflater.inflate (LayoutInflater.java:397) bei ...
e-info128
@ AvrahamShukron - Das ist da, weil ich im Vorschaumodus immer wieder einen Fehler in Android Studio bekommen habe (vermutlich, weil es nicht weiß, wie man eine benutzerdefinierte Schrift anwendet.
Themarshal
Xmlns: custom = " schemas.android.com/tools " in RelativeLayout hinzugefügt und der Fehler wurde behoben . Danke Mann
Naveed Ahmad
1
Hallo @themarshal: Anstelle von xmlns: custom = " schemas.android.com/tools " sollten Sie Folgendes verwenden: xmlns: custom = " schemas.android.com/apk/res-auto ", um stilbare Attribute zu verwenden.
Abhinav Saxena
28

Hier ist ein Beispielcode, der dies tut. Ich habe die Schriftart in einer statischen endgültigen Variablen definiert und die Schriftartdatei befindet sich im Assets-Verzeichnis.

public class TextViewWithFont extends TextView {

    public TextViewWithFont(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setTypeface(MainActivity.typeface);
    }

    public TextViewWithFont(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setTypeface(MainActivity.typeface);
    }

    public TextViewWithFont(Context context) {
        super(context);
        this.setTypeface(MainActivity.typeface);
    }

}
Stephan Wiesner
quelle
5
Das OP gibt ausdrücklich an, dass er weiß, wie man die Schriftart programmgesteuert einstellt (er liefert sogar ein Beispiel). Die Frage ist, wie man die Schriftart in XML einstellt.
SMBiggs
13

Erstellen Sie Ihre benutzerdefinierte Textansicht, die zu der Schriftart gehört, die Sie verwenden möchten. In dieser Klasse verwende ich ein statisches mTypeface-Feld, um die Schrift zwischenzuspeichern (für eine bessere Leistung).

public class HeliVnTextView extends TextView {

/*
 * Caches typefaces based on their file path and name, so that they don't have to be created every time when they are referenced.
 */
private static Typeface mTypeface;

public HeliVnTextView(final Context context) {
    this(context, null);
}

public HeliVnTextView(final Context context, final AttributeSet attrs) {
    this(context, attrs, 0);
}

public HeliVnTextView(final Context context, final AttributeSet attrs, final int defStyle) {
    super(context, attrs, defStyle);

     if (mTypeface == null) {
         mTypeface = Typeface.createFromAsset(context.getAssets(), "HelveticaiDesignVnLt.ttf");
     }
     setTypeface(mTypeface);
}

}

In der XML-Datei:

<java.example.HeliVnTextView
        android:id="@+id/textView1"
        android:layout_width="0dp"
        ... />

In der Java-Klasse:

HeliVnTextView title = new HeliVnTextView(getActivity());
title.setText(issue.getName());
Haotang
quelle
11

Die Aktivität implementiert LayoutInflater.Factory2, das Rückrufe für jede erstellte Ansicht bereitstellt. Es ist möglich, die Textansicht mit dem Attribut "Benutzerdefinierte Schriftfamilie" zu formatieren, die Schriftarten bei Bedarf zu laden und "setTypeface" für instanziierte Textansichten automatisch aufzurufen.

Aufgrund der architektonischen Beziehung von Inflater-Instanzen zu Aktivitäten und Windows besteht der einfachste Ansatz zur Verwendung benutzerdefinierter Schriftarten in Android darin, geladene Schriftarten auf Anwendungsebene zwischenzuspeichern.

Die Beispielcodebasis ist hier:

https://github.com/leok7v/android-textview-custom-fonts

  <style name="Baroque" parent="@android:style/TextAppearance.Medium">
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textColor">#F2BAD0</item>
    <item name="android:textSize">14pt</item>
    <item name="fontFamily">baroque_script</item>
  </style>

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:custom="http://schemas.android.com/apk/res/custom.fonts"
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
  >
  <TextView
    style="@style/Baroque"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/sample_text"
  />

führt zu

Geben Sie hier die Bildbeschreibung ein

Löwe
quelle
Ich habe diesen Code 2 Jahre lang nicht berührt. Aber es sollte. Ich sehe theoretisch nichts, was dies verhindert.
Leo
7

Aufgrund dieser Tatsache ist es keine gute Idee, benutzerdefinierte Schriftarten in XML zu verwenden. Das heißt, Sie müssen dies programmgesteuert tun, um Speicherverluste zu vermeiden!

Typ-a1pha
quelle
6
Es scheint, dass der Speicherverlust in Ice Cream Sandwich behoben wurde.
Michael Scheper
2
Sie sollten die TypedArray-Methode recycle () verwenden, um Speicherverluste zu vermeiden.
Abhinav Saxena
7

UPDATE: https://github.com/chrisjenx/Calligraphy scheint eine überlegene Lösung zu sein.


Vielleicht können Sie Reflection verwenden, um Ihre Schriftart in die statische Liste der verfügbaren Schriftarten einzufügen / zu hacken, wenn Ihre Anwendung erstellt wird? Ich bin an Rückmeldungen von anderen interessiert, wenn dies eine wirklich, wirklich schlechte Idee ist oder wenn dies eine großartige Lösung ist - es scheint, dass es eines dieser Extreme sein wird ...

Ich konnte meine benutzerdefinierte Schriftart mit meinem eigenen Schriftfamiliennamen in die Liste der Systemschriftarten einfügen und dann diesen benutzerdefinierten Schriftfamiliennamen ("Pinsel-Skript") als Wert für android: FontFamily in einer Standard-Textansicht angeben, die auf meinem LG funktioniert G4 mit Android 6.0.

public class MyApplication extends android.app.Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();

        Typeface font = Typeface.createFromAsset(this.getResources().getAssets(),"fonts/brush-script.ttf");
        injectTypeface("brush-script", font);
    }

    private boolean injectTypeface(String fontFamily, Typeface typeface)
    {
        try
        {
            Field field = Typeface.class.getDeclaredField("sSystemFontMap");
            field.setAccessible(true);
            Object fieldValue = field.get(null);
            Map<String, Typeface> map = (Map<String, Typeface>) fieldValue;
            map.put(fontFamily, typeface);
            return true;
        }
        catch (Exception e)
        {
            Log.e("Font-Injection", "Failed to inject typeface.", e);
        }
        return false;
    }
}

In meinem Layout

<TextView
    android:id="@+id/name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Fancy Text"
    android:fontFamily="brush-script"/>
Ross Bradbury
quelle
Funktioniert bei mir nicht. Können Sie näher erläutern, wie Sie es verwenden? Muss die MyApplicationKlasse aufgerufen werden?
Dadan
@Yawz Sie müssten die injizierendeTypeface-Methode mindestens einmal in Ihrer Anwendung aufrufen. Wenn es eine Ausnahme protokolliert, würde mich das interessieren. Ich habe dies nur auf meinem LG G4 mit Android 6.0 getestet (ich habe zu diesem Zeitpunkt einige meiner eigenen Nachforschungen angestellt) und ich gehe davon aus, dass es nicht auf allen Android-Versionen funktioniert.
Ross Bradbury
@ RossBradbury es funktioniert nicht auf einigen Geräten .. die meisten Geräte funktionieren korrekt, aber einige Geräte akzeptieren diese Schriftfamilie nicht .. mein getestetes Gerät - Lenova A7000 Modell
Ranjith Kumar
UPDATE: github.com/chrisjenx/Calligraphy ist eine überlegene Lösung.
Ross Bradbury
4

Erstellen Sie einen Schriftartenordner in den Assets und fügen Sie dort alle erforderlichen Schriftarten hinzu.

public class CustomTextView extends TextView {
    private static final String TAG = "TextView";

    public CustomTextView(Context context) {
        super(context);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setCustomFont(context, attrs);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setCustomFont(context, attrs);
    }

    private void setCustomFont(Context ctx, AttributeSet attrs) {
        TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
        String customFont = a.getString(R.styleable.CustomTextView_customFont);
        setCustomFont(ctx, customFont);
        a.recycle();
    }

    public boolean setCustomFont(Context ctx, String fontName) {
        Typeface typeface = null;
        try {
            if(fontName == null){
                fontName = Constants.DEFAULT_FONT_NAME;
            }
            typeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/" + fontName);
        } catch (Exception e) {
            Log.e(TAG, "Unable to load typeface: "+e.getMessage());
            return false;
        }
        setTypeface(typeface);
        return true;
    }
}

und fügen Sie eine deklarierbare Datei in attrs.xml hinzu

<declare-styleable name="CustomTextView">
      <attr name="customFont" format="string"/>
</declare-styleable>

und fügen Sie dann Ihren customFont like hinzu

app:customFont="arial.ttf"
Zar E Ahmer
quelle
Sie können die obige Klasse auch so anpassen, dass sie mit dem hier beschriebenen Stil arbeitet. github.com/leok7v/android-textview-custom-fonts/blob/master/res/…
Zar E Ahmer
4

Ich weiß, dass dies eine alte Frage ist, aber ich habe eine viel einfachere Lösung gefunden.

Deklarieren Sie zuerst Ihre TextView wie gewohnt in XML. Legen Sie Ihre Schriftart (TTF oder TTC) im Asset-Ordner ab

app \ src \ main \ assets \

Legen Sie dann einfach die Schriftart für Ihre Textansicht in Ihrer onCreate-Methode fest.

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

    TextView textView = findViewById(R.id.my_textView);
    Typeface typeface = Typeface.createFromAsset(getAssets(), "fontName.ttf");
    textView.setTypeface(typeface);
}

Getan.

Oiproks
quelle
1
Die Frage besagt eindeutig, dass es in XML-Dateien nicht programmgesteuert verwendet werden soll
Gustavo Baiocchi Costa,
0

anstelle von xmlns: custom = "schemas.android.com/tools"; Sie sollten verwenden: xmlns: custom = "schemas.android.com/apk/res-auto"; um stilbare Attribute zu verwenden. Ich habe diese Änderung vorgenommen und sie funktioniert jetzt.

Abhinav Saxena
quelle