전체 응용 프로그램에 대해 사용자 정의 글꼴을 설정할 수 있습니까?


270

전체 응용 프로그램에 특정 글꼴을 사용해야합니다. 동일한 .ttf 파일이 있습니다. 응용 프로그램 시작시이 글꼴을 기본 글꼴로 설정 한 다음 응용 프로그램의 다른 곳에서 사용할 수 있습니까? 설정하면 레이아웃 XML에서 어떻게 사용합니까?


안드로이드 스튜디오 3.0 당신은 쉽게 사용자 정의 글꼴을 설정할 수 있습니다 developer.android.com/guide/topics/ui/look-and-feel/...
MHSFisher

XML에서 @MHSFisher 글꼴은 안드로이드 8.0 (API 레벨 26)에 대한 기능입니다
에미 말이 맞아

1
@EmiRaz는 doc developer.android.com/guide/topics/ui/look-and-feel/… 를 읽으십시오 . 이 기능은 지원 라이브러리 26에 추가되었지만 API 16에서 지원됩니다.
MHSFisher

답변:


449

그렇습니다. 이것은 작동합니다 ( 이 답변을 기반으로 ).

(참고 : 이것은 사용자 정의 글꼴에 대한 지원이 없기 때문에 해결 방법 이므로이 상황을 변경하려면 여기 에서 Android 문제를 투표 하십시오 .) 참고 : 해당 문제에 대해 "나도"의견을 남기지 마십시오 . 별표를 표시 한 모든 사람은 이메일을받을 때 이메일을받습니다. 그러니 그냥 "별"입니다.

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

그런 다음 몇 가지 기본 글꼴 (예 : 응용 프로그램 클래스) 을 오버로드해야합니다 .

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

또는 동일한 글꼴 파일을 사용하는 경우이를 개선하여 한 번만로드 할 수 있습니다.

그러나 나는 하나를 무시 "MONOSPACE"하고 글꼴 서체 응용 프로그램을 강제로 적용하는 스타일을 설정하는 경향이 있습니다.

<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 안드로이드 5.0

작동하지 않는다는 의견의 보고서를 조사했으며 테마와 호환되지 않는 것으로 보입니다. android:Theme.Material.Light .

해당 테마가 중요하지 않은 경우 다음과 같은 이전 테마를 사용하십시오.

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

11
모노 스페이스 굵은 체 또는 이탤릭체를 어떻게 재정의 하시겠습니까?
Christopher Rivera

2
@ChristopherRivera "DEFAULT_BOLD"개인 필드도 있습니다 : private static Typeface[] sDefaults;리플렉션하고 sDefaults[Typeface.ITALIC]yourTypeface로 설정하여이 필드에 액세스 할 수 있습니다. 참조 이.
weston

5
@weston 걱정 마세요. 나는 그것을 달성했다. 이해하는 데 실수가있었습니다. 당신은 훌륭한 일을했습니다. 한 가지만 말씀해 주시겠습니까? DEFAULT Typeface에서 작동하지 않는 이유는 무엇입니까? 귀하의 제안으로 Typeface를 MONOSPACE로 변경 한 다음 loginc를 적용하면 효과가 있습니다. 그러나 DEFAULT 작동하지
제이 디야

2
기본 서체를 오류로 만들 수 없습니다. 글꼴 경로를 추가하여 수정했습니다. "FontsOverride.setDefaultFont (this,"DEFAULT ","fonts / MyFontAsset.ttf ");"
Sai

3
TextView사용자 정의 글꼴을 사용하거나 이와 같은 리플렉션 기반 솔루션을 사용하기 위해 모든 곳 에서 기본값을 바꾸어야한다는 것은 정말 성가신 일 입니다. 안드로이드에서 사용할 수있는 제한적인 글꼴 세트와 결합하여 멋진 앱을 개발하는 데 많은 노력을 기울입니다. Android 이슈 트래커에 기능 요청을 추가했습니다. 이 기능을보고 싶다면 code.google.com/p/android/issues/…
Sam

64

안드로이드에는 커스텀 폰트를위한 훌륭한 라이브러리가 있습니다 : Calligraphy

다음은 사용 방법에 대한 샘플입니다.

Gradle 에서이 줄을 앱의 build.gradle 파일에 넣어야합니다.

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

그런 다음 Application이 코드 를 확장 하고 작성 하는 클래스를 만듭니다 .

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

그리고 활동 클래스 에서이 메소드를 onCreate 전에 두십시오.

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

매니페스트 파일의 마지막 모습은 다음과 같습니다.

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

전체 활동을 글꼴로 변경합니다! 간단하고 깨끗합니다!


1
이것은 완벽한 솔루션입니다.
nizam.sp 2019

1
사실, 매우 완벽한 솔루션입니다.이 방법으로 메뉴 항목, 라디오 버튼 또는 확인란의 글꼴을 업데이트하기 위해 다른 클래스를 작성할 필요가 없습니다. 모두 적용됩니다! thanks :)
Manisha

2
확실히 완벽한 해결책은 아닙니다 : 굵은 체 / 이탤릭체 / 굵은 체-이탈리아 스타일을 덮어 씁니다. 현재 그러한 가족을 설정하는 쉬운 방법은 없습니다.
0101100101

2
그렇게하면 어떻게 굵게에서 일반으로 전환 할 수 있습니까?
GMX

2
굵게, 기울임 꼴 또는 밑줄 기능을 얻으려면, 내가 사용했습니다 <b></b>, <u></u>그리고 <i></i>에서 strings.xml파일과 지금까지 작동합니다.
jlively

47

전체 응용 프로그램에서는 작동하지 않지만 활동에서는 작동하고 다른 활동에서는 재사용 할 수 있습니다. 다른 뷰를 지원하기 위해 @ FR073N 덕분에 코드를 업데이트했습니다. 의 문제에 대해 잘 모르겠습니다 Buttons.RadioGroups 등 해당 클래스의 모든 확장 때문에TextView 그들이 잘 작동해야하므로. 리플렉션 사용에 대한 부울 조건을 추가했습니다. 왜냐하면 매우 해킹처럼 보이고 성능을 현저하게 저하시킬 수 있기 때문입니다.

참고 : 지적한 것처럼 동적 콘텐츠에는 작동하지 않습니다! 이를 위해 onCreateViewor getView메소드를 사용하여이 메소드를 호출 할 수 있지만 추가 노력이 필요합니다.

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

그런 다음 사용하려면 다음과 같이하십시오.

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

희망이 도움이됩니다.


4
가장 좋은 방법은 textview를 재정의하는 것입니다. 뷰가 많은 응용 프로그램이있는 경우이 방법이 다소 중복 될 수 있습니다.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
가능하지만 ... Android 사양에 따르면 4 레벨보다 깊고 100 뷰 너비의 레이아웃을 피하고 싶습니다 (심지어는 조금도 있음). 재귀는 성능면에서 나쁠 수 있지만 레이아웃이 20 레벨이더라도 중요하지 않습니다.
Tom

나는 그것이 성능에 눈에 띄는 영향을 미치지 않으며 귀하의 답변이 잘못되었다고 말하지는 않는다는 데 동의합니다. 필요하지 않으면 레이아웃의 모든 뷰를 순환하는 것을 좋아하지 않습니다. 또한 TextView를 확장하여 다른 방식으로 모양을 수정하려면 한 지점에서만 코드를 변경하면됩니다.
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

1
위의 코드는 TextViews만을 다루고 싶지만 ListViews, Alerts, Toasts, Map Markers 등은 여전히 ​​시스템 글꼴을 사용합니다.
csch

2
이 함수는 재귀 적으로 호출 할 때 2 개의 매개 변수를 전달했을 때 3 개의 매개 변수를 사용합니다 ??? 어느 것이 올바른가요? 무엇을 반영합니까 ??
MBH

34

요약해서 말하자면:

옵션 # 1 : 리플렉션을 사용하여 글꼴 적용 ( 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();
            } 
        }
    }

} 

응용 프로그램 클래스의 사용법 :

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

글꼴 서체 응용 프로그램을 광범위하게 적용하는 스타일을 설정하십시오 ( lovefish 기반 ).

롤리팝 전 :

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

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

롤리팝 (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>

Option2 : 폰트를 커스터마이즈해야하는 각각의 모든 뷰를 서브 클래 합니다. ListView, EditTextView, Button 등 ( 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);
    } 
} 

옵션 3 : 현재 화면의 뷰 계층을 통과하는 뷰 크롤러를 구현합니다.

변형 # 1 ( 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... */ }
        } 
    } 
} 

사용법 :

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

변형 # 2 : https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere .

옵션 # 4 : Calligraphy 라는 타사 라이브러리를 사용하십시오 .

개인적으로, 옵션 # 4는 많은 두통을 덜기 때문에 권장합니다.


에서 옵션 # 1 , 당신은 무엇을 의미합니까 "sans-serif"에서 newMap.put(, 그리고 monospacestyles?! 이름이 커스텀 글꼴을 사용하고 싶습니다 barana.ttf.
Dr.jacky

옵션 1에서 첫 번째 코드 스 니펫은 API 레벨 22, Android 5.1.1에서 작동하지 않습니다. 그러나 다른 코드 섹션은 그렇지 않습니다. 그렇다면 정확한 조건은 무엇입니까?
user1510006

28

웨스턴 을 개선하고 싶습니다API 21 Android 5.0에 대한 의 답변 .

원인

API 21에서 대부분의 텍스트 스타일에는 다음과 같은 fontFamily 설정이 포함됩니다.

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

기본 Roboto Regular 글꼴을 적용합니다.

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

android : fontFamily가 android : typeface 속성 ( reference 보다 우선 순위가 높기 때문에 원래의 대답은 고정 폭 글꼴을 적용하지 못합니다. ) . 내부에 android : fontFamily 설정이 없으므로 Theme.Holo. *를 사용하는 것이 올바른 해결 방법입니다.

해결책

Android 5.0은 시스템 서체를 정적 변수 Typeface.sSystemFontMap ( reference ) 에 넣었으므로 동일한 반사 기법을 사용하여 대체 할 수 있습니다.

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
그러나 L에 대한 좋은 해결 방법, 왜 이것이 내 Button및 도구 모음 제목 에서 작동하지 않는지 궁금 합니다. MainApplication의 기본 글꼴을 다음과 같이 재정의하고 FontsOverride.setDefaultFont(this, "DEFAULT", "MyFontAsset.ttf");있습니다. Nexus 5, v5.1.1을 사용하고 있습니다.
kip2

3
Hey, Thank you 이것은 매우 도움이되었습니다. 단 한 가지 : Android 6 (API 22)에서는 작동하지 않습니다. 그런 다음 코드를 다음과 같이 변경해야합니다. Build.VERSION.SDK_INT == 21- API 21 API 22를 사용하여 Nexus에서 테스트
Saeid

여전히 Nexus 9에서이 방법을 사용하여 맞춤 글꼴을 설정할 수 없습니다.
Rahul Upadhyay

2
5.1+에서 작동하지 않는 데 문제가있는 사람 values-v21 스타일의 서체를 재정의하지 마십시오.
Muhammad Babar 2019

감사합니다 @MuhammadBabar 솔루션 내 일했다
M.Yogeshwaran

15

그것의 아주 간단한 ... 1. 다운로드 및 자산에 ur 사용자 정의 글꼴을 넣습니다. 다음 텍스트 뷰에 대해 별도의 클래스를 하나 작성하십시오 : 여기에 나는 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);
    }
}

}

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

예, 작동합니다 :) 그러나 다른 나머지보기 / 위젯에 동일한 글꼴을 적용하려면 어떻게해야합니까? 대화 상자 / 토스트 / 작업 표시 줄을 포함한 다른 모든보기에 대해 별도의 클래스를 작성해야합니까?
Narendra Singh

예,이 솔루션의 경우 각보기마다 별도의 클래스를 작성해야합니다.
Saad Bilal

9

TextView 및 기타 컨트롤을 확장하는 것이 좋습니다. 그러나 구문에서 글꼴을 설정하는 것이 좋습니다.

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
4.0 이전의 플랫폼에서이 작업을 수행 할 때주의하십시오. Android의 버그로 인해 많은 리소스가 유출 될 수 있습니다. code.google.com/p/android/issues/detail?id=9904
Ryan M

어떻게 기본값으로 되돌릴 수 있습니까? 다음과 같은 일을하고 있습니다 : if (configShared.getString ( "storeId", AppCredentials.DEFAULT_STORE_ID) .equals ( "2")) {final Field staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (true); staticField.set (null, newTypeface); } else {final Field staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (true); staticField.set (null, null); }
Killer

8

테마 " Theme.AppCompat "로 API 21 이상의 Android 롤리팝에 대한 westonRoger Huang 의 답변 을 개선하고 싶습니다 .

안드로이드 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>

초과 (동일한) 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>

그리고 FontsOverride 파일 폴더의 유틸리티는 무엇에서와 동일 웨스턴 의 대답. 이 전화에서 테스트했습니다.

Nexus 5 (Android 5.1 기본 Android 시스템)

ZTE V5 (Android 5.1 CM12.1)

XIAOMI 노트 (Android 4.4 MIUI6)

화웨이 C8850 (Android 2.3.5 UNKNOWN)


8

https://coderwall.com/p/qxxmaa/android-use-a-custom-font-everywhere 에서 훌륭한 솔루션을 찾을 수 있습니다 .

BaseActivity에서 활동을 확장하고 해당 메소드를 작성하십시오. 또한 https://stackoverflow.com/a/16902532/2914140에 설명 된대로 글꼴을 더 잘 캐시해야합니다 .


몇 가지 연구를 한 후 Samsung Galaxy Tab A (Android 5.0)에서 작동하는 코드를 작성했습니다. https://stackoverflow.com/a/33236102/2914140 뿐만 아니라 weston 및 Roger Huang의 코드를 사용했습니다 . 또한 작동하지 않는 Lenovo TAB 2 A10-70L에서도 테스트되었습니다. 차이점을 확인하기 위해 글꼴 'Comic Sans'를 여기에 삽입했습니다.

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

전체 응용 프로그램에서 코드를 실행하려면 Application과 같은 클래스에서 다음을 작성해야합니다.

    new FontsOverride(this).loadFonts();

'자산'내에 '글꼴'폴더를 만들고 필요한 글꼴을 거기에 넣으십시오. 간단한 지침은 https://stackoverflow.com/a/31697103/2914140 에서 찾을 수 있습니다 .

Lenovo 장치도 서체 값을 잘못 얻습니다. 대부분의 경우 Typeface.NORMAL, 때로는 null을 반환합니다. xml 파일 레이아웃에서 TextView가 굵게 표시 되더라도. 여기를 참조하십시오 : TextView isBold는 항상 NORMAL을 반환합니다 . 이런 식으로 화면의 텍스트는 항상 굵은 기울임 꼴이 아닌 일반적인 글꼴로 표시됩니다. 그래서 나는 그것이 프로듀서의 버그라고 생각합니다.


나는 안드로이드 소스 코드를 연구했다. 당신의 대답은 가장 좋은 것이다. 얇은, 중간, 빛 또는 다른 것을 대체 할 수 있습니다. 정말 고맙습니다!
Mohammad Omidvar 오전

6

Xamarin.Android에 대한 작업 :

수업:

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

응용 프로그램 구현 :

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

스타일:

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

6

Android O부터 XML에서 직접 정의 할 수 있으며 이제 버그가 해결되었습니다!

자세한 내용은 여기를 참조하십시오

TL; DR :

먼저 프로젝트에 글꼴을 추가해야합니다

두 번째로 다음과 같이 글꼴 모음을 추가하십시오.

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

마지막으로 레이아웃이나 스타일에서 글꼴을 사용할 수 있습니다.

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

즐겨!


감사. 폰트 폴더를 추가하려면 Android Studio 2.4를 사용해야합니다.
광자 포인트

실제로 나는 그것을 내 스타일에 적용해야하며 응용 프로그램을 통해 적용됩니다. 그러나 일부 (프로그램 적으로 추가 된 컨트롤은 자바 코드를 통해 적용되어야 함)
SolidSnake

4

루트 뷰를 전달하여 모든 레이아웃에서 하나의 함수 호출만으로 모든 레이아웃에 대해 사용자 정의 글꼴을 하나씩 설정할 수 있습니다. 먼저 다음과 같은 글꼴 개체에 액세스하기위한 단일 방법을 만듭니다.

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

}

위 클래스에서 다른 글꼴을 정의 할 수 있습니다. 이제 글꼴을 적용 할 글꼴 도우미 클래스를 정의하십시오.

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

        }

    }

}

위의 코드에서 textView 및 EditText에만 글꼴을 적용하고 다른보기 요소에도 글꼴을 적용 할 수 있습니다. 루트보기 그룹의 id를 위의 글꼴 적용 메소드에 전달하면됩니다. 예를 들어 레이아웃은 다음과 같습니다.

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

위의 레이아웃에서 루트 부모 ID는 "주 부모"이며 이제 글꼴을 적용 할 수 있습니다

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

건배 :)


Nice .. 더 이상 같은 글꼴을 다시 만들지 않고 모든 필드에 하나의 글꼴 만 사용합니다. :)
Arfan Mirza

3

TextView를 확장하고 항상 XML 레이아웃 내에서 또는 TextView가 필요한 모든 곳에서 사용자 정의 TextView를 사용하는 것이 좋습니다. 사용자 정의 TextView에서 재정의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

Tom의 솔루션은 훌륭하게 작동하지만 TextView 및 EditText에서만 작동합니다.

대부분의 뷰 (RadioGroup, TextView, Checkbox ...)를 다루고 싶다면 다음과 같은 방법을 만들었습니다.

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

이 메소드는 XML로 설정된 뷰 스타일 (굵게, 기울임 꼴 ...)을 가져 와서 존재하는 경우 적용합니다.

ListView의 경우 항상 어댑터를 만들고 getView 내에 글꼴을 설정합니다.


2

현재 뷰 계층의 뷰에 서체를 할당하는 클래스를 작성하고 현재 서체 속성을 기반으로합니다 (굵고, 보통, 원하는 경우 다른 스타일을 추가 할 수 있음).

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

이제 onViewCreated 또는 onCreateView의 모든 조각에서 onCreate의 모든 활동과 getView 또는 newView의 모든보기 어댑터에서 다음을 호출하십시오.

typefaceAssigner.assignTypeface(view);

2

build.gradle 3.0.0 이상이있는 api 26에서는 res에서 글꼴 디렉토리를 만들고이 줄을 스타일에 사용할 수 있습니다

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

변경 build.gradle을 위해 build.gradle dependecies에서 이것을 사용하십시오.

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

프로젝트에 글꼴을 삽입하는 방법은 무엇입니까?
azerafati

@azerafati 글꼴을 res / font로 복사
Mohammad Hosein Heidari

그래, 탄스 나는 이미 그것을 얻었다. 그러나 item그것만으로는 전체 앱의 글꼴을 설정하지 않습니다. 어떤 단서?
azerafati

1

마지막으로 Google은이 문제의 심각성을 인식하고 (사용자 정의 글꼴을 UI 구성 요소에 적용) 깨끗한 솔루션을 고안했습니다.

먼저 라이브러리 26+를 지원하도록 업데이트해야합니다 (gradle {4.0+}, android studio도 업데이트해야 함). font라는 새 리소스 폴더를 만들 수 있습니다. 이 폴더에는 글꼴 리소스 (.tff, ...)를 넣을 수 있습니다. 그런 다음 기본 앱을 재정의하고 사용자 정의 글꼴을 강제로 적용해야합니다. :)

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

참고 : 16보다 오래된 API를 사용하는 기기를 지원하려면 Android 대신 앱 네임 스페이스를 사용해야합니다!


0

API 21 Android 5.0에 대한 weston의 답변을 향상시키고 싶습니다.

DEFAULT 글꼴을 사용할 때 Samsung s5에서도 동일한 문제가 발생했습니다. (다른 글꼴을 사용하면 제대로 작동합니다)

각 Textview 또는 Button에 대해 XML 파일에서 서체 (예 : "sans")를 설정하여 작동하도록 만들었습니다.

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

그리고 MyApplication 클래스에서 :

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

도움이 되길 바랍니다.



0

서예 는 꽤 잘 작동하지만 글꼴 모음에 대해 다른 가중치 (굵게, 기울임 꼴 등)를 지원하지 않기 때문에 나에게 적합하지 않습니다.

그래서 Fontain을 사용해 보았습니다. 사용자 정의 뷰를 정의하고 사용자 정의 글꼴 패밀리를 적용 할 수 있습니다.

Fontain을 사용하려면 앱 모듈 build.gradle에 다음을 추가해야합니다.

compile 'com.scopely:fontain:1.0.0'

그런 다음 일반 TextView를 사용하는 대신 FontTextView를 사용해야합니다.

대문자 및 굵은 체 컨텐츠가있는 FontTextView의 예 :

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

    }

}

완벽한 솔루션이 아닙니다. 액세스 기본 안드로이드 사용자 정의 해결 방법이 필요합니다이 솔루션을 사용하여 TextViewS
blueware

0

TextView의 기본 글꼴 모음을 변경하려면 앱 테마에서 textViewStyle을 재정의하십시오.

fontFamily에서 사용자 정의 글꼴을 사용하려면 지원 라이브러리에있는 글꼴 자원을 사용하십시오.

이 기능은 Android 26에서 추가되었지만 supportlib를 통해 이전 버전으로 백 포트되었습니다.

https://developer.android.com/guide/topics/resources/font-resource.html https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html # using-support-lib


0

Android Oreo 및 지원 라이브러리 (26.0.0)가 출시 된 이후에 쉽게 수행 할 수 있습니다. 이 답변을 참조하십시오다른 질문 을 .

기본적으로 최종 스타일은 다음과 같습니다.

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


-15

예, 글꼴을 전체 응용 프로그램으로 설정할 수 있습니다.

가장 쉬운 방법은 원하는 글꼴을 응용 프로그램과 함께 패키지하는 것입니다.

이렇게하려면 간단히 자산 / 프로젝트 루트에 폴더를 글꼴 (TrueType 또는 TTF 형식)을 자산에 넣으십시오.

예를 들어, asset / fonts /를 작성 하고 TTF 파일을 거기에 넣을 수 있습니다.

public class FontSampler extends Activity {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
TextView tv=(TextView)findViewById(R.id.custom);

Typeface face=Typeface.createFromAsset(getAssets(), "fonts/HandmadeTypewriter.ttf");
tv.setTypeface(face);
}
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.