Menggunakan font khusus di TextView android menggunakan xml

93

Saya telah menambahkan file font khusus ke folder aset / font saya. Bagaimana cara menggunakannya dari XML saya?

Saya dapat menggunakannya dari kode sebagai berikut:

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

Tidak bisakah saya melakukannya dari XML menggunakan android:typeface="/fonts/Molot.otf"atribut?

Nilanchal
sumber
Saya telah banyak mencari ini dan tidak mungkin Anda dapat melakukannya dari xml.
Cd
2
coba periksa posting ini stackoverflow.com/questions/2376250/…
dor506
Lihat jawaban ini ! Ini memungkinkan Anda menggunakan banyak font dan menggunakan XML.
Rafa0809
Seperti yang dikatakan orang lain di bawah ini, Anda dapat menggunakan Kaligrafi untuk mencapai ini.
mbonnin
Periksa artikel ini gadgetsaint.com/tips/…
ASP

Jawaban:

45

Jawaban singkat: Tidak. Android tidak memiliki dukungan bawaan untuk menerapkan font khusus ke widget teks melalui XML.

Namun, ada solusi yang tidak terlalu sulit untuk diterapkan.

Pertama

Anda harus menentukan gaya Anda sendiri. Di folder / res / values ​​Anda, buka / buat file attrs.xml dan tambahkan objek yang dapat dideklarasikan bergaya seperti ini:

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

Kedua

Dengan asumsi Anda ingin sering menggunakan widget ini, Anda harus menyiapkan cache sederhana untuk Typefaceobjek yang dimuat , karena memuatnya dari memori dengan cepat dapat memakan waktu. Sesuatu seperti:

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

Yang ini akan memungkinkan Anda untuk menggunakan ekstensi file .ttc, tetapi belum teruji.

Ketiga

Buat kelas baru yang merupakan subkelas TextView. Contoh khusus ini memperhitungkan jenis huruf XML yang ditentukan ( bold,, italicdll.) Dan menerapkannya ke font (dengan asumsi Anda menggunakan file .ttc).

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

Akhirnya

Ganti contoh TextViewdalam XML Anda dengan nama kelas yang memenuhi syarat. Deklarasikan namespace kustom Anda seperti yang Anda lakukan pada namespace Android. Perhatikan bahwa "typefaceAsset" harus mengarah ke file .ttf atau .ttc yang ada di direktori / assets Anda.

<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
sumber
Jawaban yang bagus! Namun satu pertanyaan: Mengapa Anda kembali ketika isInEditMode?
Avi Shukron
04-11 18: 18: 32.685 3506-3506 / com.example.demo E / AndroidRuntime ﹕ PENGECUALIAN FATAL: main Proses: com.example.demo, PID: 3506 android.view.InflateException: Binary XML file line # 2: Error menggembungkan kelas com.example.demo.libraries.LatoTextView di android.view.LayoutInflater.createView (LayoutInflater.java:620) di android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:696) di android.view.LayoutInflater.inflate ( LayoutInflater.java:469) di android.view.LayoutInflater.inflate (LayoutInflater.java:397) di ...
e-info128
@AvrahamShukron - Itu di sana karena saya terus mendapatkan kesalahan di Android Studio saat dalam mode pratinjau (mungkin karena tidak tahu cara menangani penerapan jenis huruf khusus.
themarshal
Menambahkan xmlns: custom = " schemas.android.com/tools " di RelativeLayout dan error telah hilang. Thanks man
Naveed Ahmad
1
Halo @themarshal: alih-alih xmlns: custom = " schemas.android.com/tools " Anda harus menggunakan: xmlns: custom = " schemas.android.com/apk/res-auto " untuk menggunakan atribut bergaya.
Abhinav Saxena
28

Berikut adalah contoh kode yang melakukan ini. Saya memiliki font yang ditentukan dalam variabel akhir statis dan file font ada di direktori aset.

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
sumber
5
OP secara khusus menyatakan bahwa dia tahu bagaimana mengatur font secara programatik (dia bahkan memberikan contoh). Pertanyaannya adalah bagaimana mengatur font dalam xml?
SMBiggs
13

Buat TextView kustom Anda milik font yang ingin Anda gunakan. Di kelas ini, saya menggunakan bidang mTypeface statis untuk menyimpan jenis huruf dalam cache (untuk kinerja yang lebih baik)

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

}

Dalam file xml:

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

Di kelas java:

HeliVnTextView title = new HeliVnTextView(getActivity());
title.setText(issue.getName());
haotang
sumber
11

Aktivitas mengimplementasikan LayoutInflater.Factory2 yang menyediakan callback pada setiap View yang dibuat. Anda dapat mengatur gaya TextView dengan atribut font Family kustom, memuat tipografi sesuai permintaan dan memanggil setTypeface pada tampilan teks yang dibuat secara otomatis.

Sayangnya karena hubungan arsitektural instans Inflater relatif terhadap Aktivitas dan Windows, pendekatan paling sederhana untuk menggunakan font khusus di android adalah dengan menyimpan font yang dimuat di cache pada level Aplikasi.

Basis kode sampel ada di sini:

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

menghasilkan

masukkan deskripsi gambar di sini

Leo
sumber
Tidak menyentuh kode itu selama 2 tahun. Tapi seharusnya begitu. Saya tidak melihat teori apa pun yang mencegahnya.
Leo
7

Bukan ide yang baik untuk menggunakan font khusus dalam xml karena fakta ini, Anda harus melakukannya secara terprogram untuk menghindari kebocoran memori!

tipe-a1pha
sumber
6
Tampaknya kebocoran memori telah diperbaiki di Ice Cream Sandwich.
Michael Scheper
2
Anda harus menggunakan metode TypedArray recycle () untuk menghindari kebocoran memori.
Abhinav Saxena
7

PEMBARUAN: https://github.com/chrisjenx/Calligraphy tampaknya menjadi solusi terbaik untuk ini.


Mungkin Anda dapat menggunakan refleksi untuk menyuntikkan / meretas font Anda ke dalam daftar statis font yang tersedia saat aplikasi Anda dibuat? Saya tertarik dengan umpan balik dari orang lain jika ini benar-benar ide yang buruk atau jika ini adalah solusi yang bagus - tampaknya ini akan menjadi salah satu yang ekstrim ...

Saya dapat memasukkan jenis huruf khusus saya ke dalam daftar jenis huruf sistem dengan nama keluarga font saya sendiri, kemudian menentukan nama keluarga font khusus itu ("brush-script") sebagai nilai android: FontFamily pada TextView standar bekerja di LG saya G4 menjalankan 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;
    }
}

Dalam tata letak saya

<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
sumber
Tidak berfungsi untuk saya, dapatkah Anda memberikan lebih banyak detail cara menggunakannya? Apakah MyApplicationkelas perlu dipanggil?
Dadan
@Yawz Anda perlu memanggil metode injectTypeface setidaknya sekali dalam aplikasi Anda. Jika itu mencatat pengecualian, saya akan tertarik dengan detailnya. Saya hanya menguji ini pada LG G4 saya yang menjalankan Android 6.0 (saya melakukan beberapa penelitian saya sendiri pada saat itu) dan saya berharap ini tidak berfungsi pada semua versi Android.
Ross Bradbury
@RossBradbury tidak berfungsi pada beberapa perangkat .. sebagian besar perangkat berfungsi dengan benar tetapi beberapa perangkat tidak menerima fontfamily ini .. perangkat yang saya uji - model lenova a7000
Ranjith Kumar
UPDATE: github.com/chrisjenx/Calligraphy adalah solusi yang unggul.
Ross Bradbury
5

Buat folder font di aset dan tambahkan semua font yang Anda butuhkan di sana.

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

dan tambahkan deklarabel di attrs.xml

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

lalu tambahkan suka customFont Anda

app:customFont="arial.ttf"
AndroidGeek
sumber
Anda juga dapat menyesuaikan kelas di atas untuk bekerja dengan gaya seperti yang dilakukan di sini. github.com/leok7v/android-textview-custom-fonts/blob/master/res/…
AndroidGeek
5

Saya tahu ini adalah pertanyaan lama, tetapi saya telah menemukan solusi yang jauh lebih mudah.

Pertama-tama deklarasikan TextView Anda dalam xml seperti biasa. Letakkan font Anda (TTF atau TTC) di folder aset

app \ src \ main \ assets \

Kemudian setel jenis huruf untuk tampilan teks Anda dalam metode onCreate.

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

Selesai.

Oiproks
sumber
1
Pertanyaannya dengan jelas menyatakan untuk menggunakannya di dalam file xml bukan secara programatik
Gustavo Baiocchi Costa
0

bukannya xmlns: custom = "schemas.android.com/tools"; Anda harus menggunakan: xmlns: custom = "schemas.android.com/apk/res-auto"; untuk menggunakan atribut bergaya. Saya membuat perubahan ini dan berhasil sekarang.

Abhinav Saxena
sumber