Có thể đặt phông chữ tùy chỉnh cho toàn bộ ứng dụng không?


270

Tôi cần sử dụng phông chữ nhất định cho toàn bộ ứng dụng của mình. Tôi có tập tin .ttf cho cùng. Có thể đặt đây là phông chữ mặc định, tại ứng dụng khởi động và sau đó sử dụng nó ở nơi khác trong ứng dụng? Khi được đặt, làm cách nào để sử dụng nó trong các XML bố trí của tôi?


trong android studio 3.0, bạn có thể dễ dàng đặt phông chữ tùy chỉnh: developer.android.com/guide/topics/ui/look-and-feel/ chủ
MHSFisher 17/12/18

@MHSFisher Phông chữ trong XML là một tính năng dành cho Android 8.0 (API cấp 26)
Emi Raz

1
@EmiRaz vui lòng đọc doc developer.android.com/guide/topics/ui/look-and-feel/ . tính năng này được thêm vào trong thư viện hỗ trợ 26, nhưng hỗ trợ từ API 16.
MHSFisher

Câu trả lời:


449

Có với sự phản ánh. Điều này hoạt động ( dựa trên câu trả lời này ):

(Lưu ý: đây là một cách giải quyết do thiếu hỗ trợ cho phông chữ tùy chỉnh, vì vậy nếu bạn muốn thay đổi tình huống này, vui lòng gắn dấu sao để bình chọn vấn đề Android tại đây ). Lưu ý: Đừng để lại bình luận "tôi cũng vậy" về vấn đề đó, tất cả những người đã nhìn chằm chằm đều nhận được email khi bạn làm điều đó. Vì vậy, chỉ cần "sao" nó.

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

Sau đó, bạn cần quá tải một vài phông chữ mặc định, ví dụ như trong một lớp ứng dụng :

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

Hoặc tất nhiên nếu bạn đang sử dụng cùng một tệp phông chữ, bạn có thể cải thiện điều này để tải nó chỉ một lần.

Tuy nhiên, tôi có xu hướng chỉ ghi đè một, nói "MONOSPACE", sau đó thiết lập một kiểu để buộc ứng dụng kiểu chữ đó rộng ra:

<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

Tôi đã điều tra các báo cáo trong các ý kiến ​​rằng nó không hoạt động và nó dường như không tương thích với chủ đề android:Theme.Material.Light .

Nếu chủ đề đó không quan trọng với bạn, hãy sử dụng một chủ đề cũ hơn, ví dụ:

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
    <item name="android:typeface">monospace</item>
</style>

11
Làm thế nào bạn sẽ ghi đè lên không gian in đậm hoặc in nghiêng?
Christopher Rivera

2
@ChristopherRivera Có "DEFAULT_BOLD"nhưng cũng có một lĩnh vực riêng tư: private static Typeface[] sDefaults;Bạn có thể thử truy cập trang này bằng cách phản chiếu và đặt sDefaults[Typeface.ITALIC]thành yourTypeface. Xem cái này
weston

5
@weston Đừng bận tâm. Tôi đã đạt được nó. Có một sai lầm trong sự hiểu biết của tôi. Bạn đã làm được một công việc tuyệt vời. Bạn có thể chỉ cho tôi một điều? Tại sao nó không hoạt động cho kiểu chữ DEFAULT? Tôi đã thay đổi Kiểu chữ thành MONOSPACE theo đề xuất của bạn và sau đó áp dụng loginc, nó đã hoạt động. Nhưng không làm việc cho DEFAULT
Jay Pandya

2
Kiểu chữ bản địa không thể được thực hiện lỗi. Đã sửa lỗi, bằng cách thêm đường dẫn phông chữ. "FontsOverride.setDefaultFont (này," DEFAULT "," phông chữ / MyFontAsset.ttf ");"
Sai

3
Tôi thấy thực sự khó chịu khi phải trao đổi mặc định TextViewở mọi nơi để sử dụng phông chữ tùy chỉnh hoặc sử dụng giải pháp dựa trên sự phản chiếu như thế này. Kết hợp với bộ phông chữ giới hạn có sẵn trong Android, nó làm cho việc phát triển các ứng dụng đẹp mắt trở nên khó khăn. Tôi đã thêm một yêu cầu tính năng cho trình theo dõi vấn đề Android. Nếu bạn muốn xem tính năng này, bạn có thể đánh dấu sao vấn đề ở đây: code.google.com/p/android/issues/iêu
Sam

64

Có một thư viện tuyệt vời cho các phông chữ tùy chỉnh trong Android: Thư pháp

đây là một mẫu làm thế nào để sử dụng nó

trong Gradle, bạn cần đặt dòng này vào tệp build.gradle của ứng dụng:

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

và sau đó tạo một lớp mở rộng Applicationvà viết mã này:

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

và trong lớp hoạt động đặt phương thức này trước onCreate:

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

và điều cuối cùng là tệp kê khai của bạn sẽ trông như thế này:

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

và nó sẽ thay đổi toàn bộ hoạt động sang phông chữ của bạn! thật đơn giản và sạch sẽ!


1
Đây là giải pháp hoàn hảo.
nizam.sp

1
Đúng, giải pháp rất hoàn hảo của nó, theo cách này tôi không phải viết các lớp khác nhau để cập nhật phông chữ cho các mục menu, nút radio hoặc hộp kiểm tra. Nó áp dụng cho tất cả !! cảm ơn :)
Manisha

2
Chắc chắn không phải là một giải pháp hoàn hảo: Các kiểu in đậm / in nghiêng / in đậm được ghi đè. Hiện tại không có cách dễ dàng để thiết lập những gia đình.
0101100101

2
Nếu tôi làm điều đó, làm thế nào tôi có thể chuyển từ Bold sang thường xuyên?
GMX

2
Để có được tính năng in đậm, in nghiêng hoặc gạch dưới, tôi đã sử dụng <b></b>, <u></u><i></i>trong strings.xmltập tin và cho đến nay nó hoạt động.
jlively

47

Mặc dù điều này không hoạt động cho toàn bộ ứng dụng, nhưng nó sẽ hoạt động cho một Hoạt động và có thể được sử dụng lại cho bất kỳ Hoạt động nào khác. Tôi đã cập nhật mã của mình nhờ @ FR073N để hỗ trợ các Chế độ xem khác. Tôi không chắc chắn về các vấn đề với Buttons, RadioGroupsvv vì những lớp học tất cả các mở rộngTextView nên chúng chỉ hoạt động tốt. Tôi đã thêm một điều kiện boolean để sử dụng sự phản chiếu bởi vì nó có vẻ rất hack và có thể làm giảm hiệu suất đáng chú ý.

Lưu ý: như đã chỉ ra, điều này sẽ không hoạt động đối với nội dung động! Vì vậy, có thể gọi phương thức này bằng cách nói onCreateViewhoặc getViewphương thức, nhưng đòi hỏi nỗ lực bổ sung.

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

Sau đó, để sử dụng nó, bạn sẽ làm một cái gì đó như thế này:

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

Mong rằng sẽ giúp.


4
Tôi nghĩ rằng cách tốt nhất sẽ là ghi đè lên textview. Phương pháp này có thể hơi dư thừa nếu bạn có một ứng dụng có nhiều lượt xem.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
Nó có thể ... nhưng theo thông số kỹ thuật của Android, bạn muốn tránh bố cục sâu hơn 4 cấp và rộng 100 lượt xem (thậm chí là khá ít). Đệ quy có thể là xấu về hiệu suất, nhưng ngay cả khi bố cục của bạn là 20 cấp độ thậm chí không đáng kể.
Tom

Tôi đồng ý rằng nó không có tác động đáng chú ý đến sự hoàn hảo và tôi không cố nói câu trả lời của bạn là sai. Tôi chỉ không thích đạp xe qua mọi góc nhìn trong bố cục nếu tôi không cần. Ngoài ra, bằng cách mở rộng TextView, nếu bạn muốn sửa đổi giao diện theo cách khác, bạn sẽ chỉ phải thay đổi mã ở một điểm.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
Chỉ muốn thêm rằng mã ở trên chỉ bao gồm TextViews nhưng ListViews, Alerts, Toasts, Map Markers, v.v. vẫn sẽ sử dụng phông chữ hệ thống.
csch

2
Các bạn có chức năng này mất 3 tham số, khi bạn gọi nó một cách đệ quy bạn đã truyền 2 tham số ??? cái nào đúng? phản ánh là gì ??
MBH

34

Tóm tắt:

Tùy chọn # 1: Sử dụng phản chiếu để áp dụng phông chữ (kết hợp câu trả lời của weston & Roger Huang ):

import java.lang.reflect.Field;
import android.content.Context;
import android.graphics.Typeface;

public final class FontsOverride { 

    public static void setDefaultFont(Context context,
            String staticTypefaceFieldName, String fontAssetName) {
        final Typeface regular = Typeface.createFromAsset(context.getAssets(),
                fontAssetName);
        replaceFont(staticTypefaceFieldName, regular);
    } 

    protected static void replaceFont(String staticTypefaceFieldName,final Typeface newTypeface) {
        if (isVersionGreaterOrEqualToLollipop()) {
            Map<String, Typeface> newMap = new HashMap<String, Typeface>();
            newMap.put("sans-serif", newTypeface);
            try {
                final Field staticField = Typeface.class.getDeclaredField("sSystemFontMap");
                staticField.setAccessible(true);
                staticField.set(null, newMap);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            try {
                final Field staticField = Typeface.class.getDeclaredField(staticTypefaceFieldName);
                staticField.setAccessible(true);
                staticField.set(null, newTypeface);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } 
        }
    }

} 

Sử dụng trong lớp Ứng dụng:

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

thiết lập một kiểu để buộc ứng dụng kiểu chữ rộng đó (dựa trên lovefish ):

Pre-Lollipop:

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

   <!-- Application theme. -->
   <style name="AppTheme" parent="AppBaseTheme">
       <item name="android:typeface">monospace</item>
   </style>
</resources>

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>

Tùy chọn 2: Phân lớp mỗi và mọi Chế độ xem bạn cần tùy chỉnh phông chữ, nghĩa là. ListView, EditTextView, Nút, v.v ( câu trả lời của 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);
    } 
} 

Tùy chọn 3: Triển khai Trình thu thập dữ liệu xem qua hệ thống phân cấp chế độ xem của màn hình hiện tại của bạn:

Biến thể số 1 ( Câu trả lời của Tom ):

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

Sử dụng :

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

Biến thể số 2: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Tùy chọn # 4: Sử dụng Lib của bên thứ 3 được gọi là Thư pháp .

Cá nhân, tôi muốn giới thiệu Lựa chọn số 4, vì nó giúp tiết kiệm rất nhiều vấn đề đau đầu.


Trong lựa chọn số 1 , bạn có ý nghĩa gì "sans-serif"trong newMap.put(monospacetrong styles?! Tôi muốn sử dụng một phông chữ tùy chỉnh có tên barana.ttf.
Dr.jacky

trong tùy chọn 1, đoạn mã đầu tiên không hoạt động với API cấp 22, Android 5.1.1. Nhưng phần mã khác thì có. Điều gì nên chính xác nếu điều kiện như vậy?
dùng1510006

28

Tôi muốn cải thiện câu trả lời của weston cho API 21 Android 5.0.

Nguyên nhân

Trong API 21, hầu hết các kiểu văn bản bao gồm cài đặt fontF Family, như:

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

Áp dụng phông chữ Roboto thông thường mặc định:

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

Câu trả lời ban đầu không áp dụng phông chữ đơn cách, vì android: fontF Family có mức độ ưu tiên cao hơn cho thuộc tính android: typeface ( tham khảo ). Sử dụng Theme.Holo. * Là một cách giải quyết hợp lệ, vì không có cài đặt android: fontF Family bên trong.

Giải pháp

Vì Android 5.0 đặt kiểu chữ hệ thống trong biến tĩnh typeface.sSystemFontMap ( tham chiếu ), chúng tôi có thể sử dụng cùng một kỹ thuật phản chiếu để thay thế nó:

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

4
Cách giải quyết tốt cho L. tuy nhiên, tôi tự hỏi tại sao điều này dường như không hoạt động cho Buttoncác tiêu đề và Thanh công cụ của tôi . Tôi đang ghi đè phông chữ mặc định trong MainApplication như sau : FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");. Tôi đang sử dụng Nexus 5, v5.1.1
kip2

3
Xin chào, Cảm ơn bạn Điều này rất hữu ích, chỉ một điều: điều này không hoạt động ở Android 6 (API 22). Sau đó, mã phải được thay đổi thành: Build.VERSION.SDK_INT == 21- Đó chỉ là trong API 21 thử nghiệm I tại Nexus với API 22
Saeid

Tuy nhiên, tôi vẫn không thể đặt phông chữ tùy chỉnh bằng phương pháp này trong Nexus 9
Rahul Upadhyay

2
Bất kỳ ai có vấn đề không làm việc cho 5.1+. Không ghi đè kiểu chữ trong các kiểu giá trị-v21 của bạn.
Muhammad Babar

@MuhammadBabar Cảm ơn giải pháp của bạn đã làm cho ngày của tôi
M.Yogeshwaran

15

rất đơn giản ... 1.Tải xuống và đặt phông chữ tùy chỉnh của bạn vào tài sản..vậy viết một lớp riêng để xem văn bản như sau: ở đây tôi đã sử dụng phông chữ 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);
    }
}

}

và làm như sau trong 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"
      />

Vâng, nó hoạt động :) Nhưng, nếu tôi muốn áp dụng cùng một phông chữ cho các chế độ xem / widget còn lại thì sao? Tôi có cần phải viết lớp riêng cho tất cả các chế độ xem khác (bao gồm cả hộp thoại / toasts / actionbars) không?
Narendra Singh

Có, đối với giải pháp này, bạn cần viết các lớp riêng biệt cho mỗi chế độ xem của mình.
Saad Bilal

9

Tôi cũng sẽ đề nghị mở rộng TextView và các điều khiển khác, nhưng sẽ tốt hơn nếu tôi xem xét để thiết lập phông chữ trong các cấu trúc.

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

2
Hãy cẩn thận khi thực hiện việc này trên các nền tảng trước 4.0 - điều này sẽ rò rỉ rất nhiều tài nguyên do lỗi trong Android: code.google.com/p/android/issues/detail?id=9904
Ryan M

Làm thế nào chúng ta có thể trở lại mặc định. Tôi đang làm một cái gì đó như thế này: if (configShared.getString ("storeId", AppCredentials.DEFAULT_STORE_ID) .equals ("2")) {Final Field staticField = typeface. Class.getDeclaredField (staticTypefaceField staticField.setAccessible (đúng); staticField.set (null, newTypeface); } other {Final Field staticField = typeface. class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (đúng); staticField.set (null, null); }
Kẻ giết người

8

Tôi muốn cải thiện câu trả lời của westonRoger Huang cho hơn kẹo API Android 21 với chủ đề " Theme.AppCompat ".

Dưới Android 4.4

<resources>
    <style name="AppBaseTheme" parent="Theme.AppCompat.Light">
    </style>

   <!-- Application theme. -->
   <style name="AppTheme" parent="AppBaseTheme">
       <item name="android:typeface">monospace</item>
   </style>
</resources>

Hơn (bằng) 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>

Và tập tin sử dụng FontsOverride cũng giống như câu trả lời của weston . Tôi đã thử nghiệm trong những điện thoại này:

Nexus 5 (Hệ thống Android chính Android 5.1)

ZTE V5 (Android 5.1 CM12.1)

Ghi chú XIAOMI (android 4.4 MIUI6)

HUAWEI C8850 (android 2.3.5 UNKNOWN)


8

Một giải pháp tuyệt vời có thể được tìm thấy ở đây: https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

Đơn giản chỉ cần mở rộng các hoạt động từ BaseActivity và viết các phương thức đó. Ngoài ra, bạn nên lưu trữ phông chữ tốt hơn như được mô tả ở đây: https://stackoverflow.com/a/16902532/2914140 .


Sau một số nghiên cứu, tôi đã viết mã hoạt động tại Samsung Galaxy Tab A (Android 5.0). Mã được sử dụng của weston và Roger Huang cũng như https://stackoverflow.com/a/33236102/2914140 . Cũng đã thử nghiệm trên Lenovo TAB 2 A10-70L, nơi nó không hoạt động. Tôi đã chèn một phông chữ 'Comic Sans' ở đây để thấy sự khác biệt.

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

Để chạy mã trong toàn bộ ứng dụng, bạn nên viết trong một số lớp như Ứng dụng như sau:

    new FontsOverride(this).loadFonts();

Tạo một thư mục 'phông chữ' bên trong 'tài sản' và đặt các phông chữ cần thiết ở đó. Một hướng dẫn đơn giản có thể được tìm thấy ở đây: https://stackoverflow.com/a/31697103/2914140 .

Thiết bị Lenovo cũng không chính xác có giá trị của một kiểu chữ. Trong hầu hết các lần, nó trả về kiểu chữ.NORMAL, đôi khi là null. Ngay cả khi TextView được in đậm (trong bố cục tệp xml). Xem ở đây: TextView isBold luôn trả về BÌNH THƯỜNG . Bằng cách này, một văn bản trên màn hình luôn ở dạng phông chữ, không in đậm hoặc in nghiêng. Vì vậy, tôi nghĩ rằng đó là một lỗi của nhà sản xuất.


Tôi đã nghiên cứu mã nguồn Android, câu trả lời của bạn là tốt nhất. Nó có thể thay thế mỏng, trung bình, ánh sáng hoặc bất cứ điều gì khác. Cảm ơn bạn rất nhiều!
Mohammad Omidvar

6

Làm việc cho Xamarin.Android:

Lớp học:

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

Ứng dụng thực hiện:

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

Phong cách:

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

6

Kể từ Android O, giờ đây có thể xác định trực tiếp từ XML và lỗi của tôi hiện đã bị đóng!

Xem tại đây để biết chi tiết

TL; DR:

Trước tiên, bạn phải thêm phông chữ của bạn vào dự án

Thứ hai bạn thêm một họ phông chữ, như vậy:

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

Cuối cùng, bạn có thể sử dụng phông chữ theo bố cục hoặc kiểu:

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

Thưởng thức!


Cảm ơn. Chúng ta nên sử dụng Android Studio 2.4 để có thể thêm thư mục phông chữ.
Điểm Photon

thực ra tôi chỉ cần áp dụng nó cho phong cách của mình và nó sẽ được áp dụng thông qua ứng dụng. nhưng một số (điều khiển được thêm bằng lập trình sẽ cần được áp dụng thông qua mã java)
SolidSnake

4

Bạn có thể đặt phông chữ tùy chỉnh cho từng bố cục một, chỉ với một lệnh gọi từ mọi bố cục bằng cách chuyển ViewFirst gốc của nó, tạo một cách tiếp cận singelton để truy cập đối tượng phông chữ như thế này

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

}

Bạn có thể xác định các phông chữ khác nhau trong lớp trên, Bây giờ Xác định một lớp Trình trợ giúp phông chữ sẽ áp dụng các phông chữ:

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

        }

    }

}

Trong đoạn mã trên, tôi chỉ áp dụng phông chữ trên textView và EditText, bạn cũng có thể áp dụng phông chữ trên các thành phần khung nhìn khác. Bạn chỉ cần chuyển id của nhóm Xem gốc của mình sang phương thức phông chữ áp dụng ở trên. ví dụ bố cục của bạn là:

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

Trong Bố cục trên, id gốc là "Main Parent" hiện cho phép áp dụng phông chữ

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

Chúc mừng :)


Đẹp .. không tạo lại phông chữ giống nhau, chỉ sử dụng 1 phông chữ cho tất cả các trường. :)
Arfan Mirza

3

Tôi sẽ đề nghị mở rộng TextView và luôn sử dụng TextView tùy chỉnh trong bố cục XML của bạn hoặc bất cứ nơi nào bạn cần TextView. Trong TextView tùy chỉnh của bạn, ghi đèsetTypeface

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

2

Giải pháp của Tom hoạt động rất tốt, nhưng chỉ hoạt động với TextView và EditText.

Nếu bạn muốn bao phủ hầu hết các chế độ xem (Radiogroup, TextView, Checkbox ...), tôi đã tạo một phương thức thực hiện điều đó:

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

Phương pháp này sẽ lấy lại kiểu của khung nhìn được đặt trong XML (đậm, in nghiêng ...) và áp dụng chúng nếu chúng tồn tại.

Đối với ListView, tôi luôn tạo một bộ điều hợp và tôi đặt phông chữ bên trong getView.


2

Tôi đã viết một lớp gán kiểu chữ cho các khung nhìn trong hệ thống phân cấp khung nhìn hiện tại và dựa trên os các thuộc tính kiểu chữ hiện tại (đậm, bình thường, bạn có thể thêm các kiểu khác nếu bạn muốn):

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

Bây giờ trong tất cả các đoạn trong onViewCreated hoặc onCreateView, trong tất cả các hoạt động trong onCreate và trong tất cả các bộ điều hợp xem trong getView hoặc newView chỉ cần gọi:

typefaceAssigner.assignTypeface(view);

2

trong api 26 với build.gradle 3.0.0 trở lên, bạn có thể tạo một thư mục phông chữ trong res và sử dụng dòng này theo phong cách của bạn

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

để thay đổi build.gradle sử dụng điều này trong các phụ thuộc build.gradle của bạn

classpath 'com.android.tools.build:gradle:3.0.0'

và làm thế nào để chèn phông chữ vào dự án?
azerafati

@azerafati sao chép phông chữ của bạn thành res / font
Mohammad Hosein Heidari

vâng, tanx. Tôi đã có được điều đó. Nhưng itemmột mình nó không đặt phông chữ cho toàn bộ ứng dụng. có manh mối nào không?
azerafati

1

Cuối cùng, Google nhận ra mức độ nghiêm trọng của vấn đề này (áp dụng phông chữ tùy chỉnh cho các thành phần UI) và họ đã nghĩ ra một giải pháp sạch cho nó.

Trước tiên, bạn cần cập nhật để hỗ trợ thư viện 26+ (bạn cũng có thể cần cập nhật gradle {4.0+}, studio android), sau đó bạn có thể tạo một thư mục tài nguyên mới gọi là font. Trong thư mục này, bạn có thể đặt tài nguyên phông chữ của mình (.tff, ...). Sau đó, bạn cần ghi đè ứng dụng mặc định chúng và buộc phông chữ tùy chỉnh của bạn vào đó :)

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

Lưu ý: nếu bạn muốn hỗ trợ các thiết bị có API cũ hơn 16, bạn phải sử dụng không gian tên ứng dụng thay vì Android!


0

Tôi cũng muốn cải thiện câu trả lời của weston cho API 21 Android 5.0.

Tôi gặp vấn đề tương tự trên Samsung s5 của tôi, khi sử dụng phông chữ DEFAULT. (với các phông chữ khác, nó hoạt động tốt)

Tôi đã quản lý để làm cho nó hoạt động bằng cách đặt kiểu chữ (ví dụ "sans") trong các tệp XML, cho mỗi Textview hoặc Nút

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

và trong Lớp học MyApplication:

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

Hy vọng nó giúp.



0

Thư pháp hoạt động khá tốt, nhưng nó không phù hợp với tôi, vì nó không hỗ trợ các trọng lượng khác nhau (đậm, in nghiêng, v.v.) cho một họ phông chữ.

Vì vậy, tôi đã thử Fontain , cho phép bạn xác định Chế độ xem tùy chỉnh và áp dụng cho họ họ phông chữ tùy chỉnh.

để sử dụng Fontain, bạn nên thêm phần sau vào mô-đun ứng dụng build.gradle:

compile 'com.scopely:fontain:1.0.0'

Sau đó, thay vì sử dụng TextView thông thường, bạn nên sử dụng FontTextView

Ví dụ về FontTextView với nội dung in hoa và in đậm:

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

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

    }

}

Không phải là một giải pháp hoàn hảo. Sử dụng giải pháp này đòi hỏi phải có cách giải quyết tùy chỉnh để truy cập mặc định Android TextViews
blueware

0

Để thay đổi họ phông chữ mặc định của TextViews, ghi đè textViewStyle trong chủ đề ứng dụng của bạn.

Để sử dụng phông chữ tùy chỉnh trong fontF Family, hãy sử dụng tài nguyên phông chữ trong thư viện hỗ trợ.

Tính năng này đã được thêm vào Android 26 nhưng được đưa vào các phiên bản cũ hơn thông qua supportlib.

https://developer.android.com/guide/topics/resource/font-resource.html https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html # sử dụng-hỗ trợ-lib


0

Kể từ khi phát hành Android Oreo và thư viện hỗ trợ của nó (26.0.0), bạn có thể thực hiện việc này một cách dễ dàng. Tham khảo câu trả lời này trong một câu hỏi khác.

Về cơ bản phong cách cuối cùng của bạn sẽ trông như thế này:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
   <item name="fontFamily">@font/your_font</item> <!-- target android sdk versions < 26 and > 14 -->
</style>


-15

Có, có thể đặt phông chữ cho toàn bộ ứng dụng.

Cách dễ nhất để thực hiện việc này là đóng gói (các) phông chữ mong muốn với ứng dụng của bạn.

Để làm điều này, chỉ cần tạo một tài sản / thư mục trong thư mục gốc của dự án và đặt phông chữ của bạn (dưới dạng TrueType hoặc TTF,) vào tài sản.

Ví dụ, bạn có thể tạo tài sản / phông chữ / và đặt các tệp TTF của mình vào đó.

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);
}
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.