2010-04-26 4 views
237

Мне нужно использовать определенный шрифт для всего моего приложения. У меня есть файл .ttf для него. Можно ли установить это как шрифт по умолчанию, при запуске приложения, а затем использовать его в другом месте приложения? Когда он установлен, как его использовать в моих XML-макетах?Можно ли установить собственный шрифт для всего приложения?

+0

Проверить это http://www.gadgetsaint.com/android/custom-fonts-textview-edittext-android/#.WNive2996Hs – ASP

ответ

-14

Да, его можно установить шрифт для всего приложения.

Самый простой способ добиться этого - упаковать нужные шрифты (ы) с вашим приложением.

Чтобы сделать это, просто создать активы/ папку в корневом каталоге проекта, и поставить шрифты (в TrueType или TTF, форма) в активах.

Вы можете, например, создать активы/шрифты/ и разместить там файлы 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); 
} 
} 
47

Хотя это не будет работать для целого приложения, оно будет работать для Activity и может быть повторно использовано для любого другого Activity. Я обновил свой код благодаря @ FR073N для поддержки других представлений. Я не уверен в проблемах с Buttons, RadioGroups и т. Д., Потому что эти классы расширяют TextView, поэтому они должны работать нормально. Я добавил логическое условие для использования отражения, потому что оно кажется очень хакерским и может значительно ухудшить производительность.

Примечание: как указано, это не будет работать для динамического контента! Для этого этот метод можно назвать с помощью метода onCreateView или 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. Этот метод может быть несколько избыточным, если у вас есть приложение с большим количеством просмотров. – zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

+1

Это может ... но в соответствии с спецификациями Android вы хотите избежать макетов глубже 4 уровней и 100 просмотров в ширину (даже это совсем немного). Рекурсия может быть плохой с точки зрения производительности, но даже если ваш макет был 20 уровней, которые даже не значительны. – Tom

+0

Я бы согласился, что это не оказывает заметного влияния на производительность, и я не пытаюсь сказать, что ваш ответ неверен. Мне просто не нравится ездить на велосипеде по каждому виду в макете, если мне это не нужно. Кроме того, расширяя TextView, если вы хотите изменить внешний вид другим способом, вам нужно будет только изменить код в одном месте. – zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

3

Я бы предложил расширить TextView и всегда использовать свой собственный TextView в ваших XML-макетах или везде, где вам нужен 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); 
} 
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: https://code.google.com/p/ android/issues/detail? id = 9904 –

+0

Как обо всех других представлениях, которые используют шрифт .. –

+0

как мы можем вернуться к умолчанию. Я делаю что-то вроде этого: if (configShared.getString ("storeId", AppCredentials.DEFAULT_STORE_ID) .equals ("2")) {final Field staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (истина); staticField.set (null, newTypeface); } else {final Поле staticField = Typeface.class.getDeclaredField (staticTypefaceFieldName); staticField.setAccessible (истина); staticField.set (null, null); } – Killer

430

Да с отражением. Эта работа (based on this answer):

(Примечание: это временное решение из-за отсутствия поддержки пользовательских шрифтов, поэтому, если вы хотите изменить эту ситуацию, пожалуйста, сделайте выбор, чтобы проголосовать за android issue here). Примечание: Не оставляйте комментарии «я тоже» по этой проблеме, каждый, кто уставился на это, получает электронное письмо, когда вы это делаете. Так что просто «звезда», пожалуйста.

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

Затем нужно перегружать несколько шрифтов по умолчанию, например, в application классе:

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 Android 5,0

Я исследовал отчеты в комментарии, что он не работает и кажется несовместимым с темой android:Theme.Material.Light.

Если эта тема не важна для вас, используйте старую тему, например .:

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

Это должен быть принятый ответ. Это хак-класс, но он просто работает. –

+1

Портировано на C# (xamarin monodroid) - отлично работает! –

+1

Очень умное решение! Выполняет риск изменения имен Typeface или переноса на новые классы, но я предполагаю, что они были такими, и где они находятся с Android 1.5, поэтому это разумно безопасный взлом.Вы можете кэшировать поля по имени для лучшей производительности (я думаю, в случае, когда приложение имеет темы и позволяет менять шрифты с помощью живой/мгновенной обратной связи). – Tom

2

решение Тома прекрасно работает, но работает только с 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.

15

его очень просто ... 1.Download и поставить ур специальный шрифт в assets..then написать один отдельный класс для просмотра текста следующим образом: здесь я использовал шрифт 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" 
     /> 
+0

Да, это работает :) Но что, если я хочу применять одни и те же шрифты для остальных остальных видов/виджетов? Нужно ли писать отдельный класс для всех других видов (включая диалоги/тосты/панели действий)? – NarendraJi

+0

Да, для этого решения вам нужно написать отдельные классы для каждого из ваших представлений. –

27

Я хотел бы улучшить weston «s ответ на 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). Использование Theme.Holo. * Является допустимым обходным решением, потому что внутри него нет настроек android: fontFamily.

Решение

Поскольку система Android 5,0 положить начертания в статической переменной начертания шрифта.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 и Tool Bar». Я переопределяю шрифт по умолчанию в MainApplication следующим образом: 'FontsOverride.setDefaultFont (это,« DEFAULT »,« MyFontAsset.ttf »);'. Я использую Nexus 5, v5.1.1 – kip2

+2

Привет, Спасибо Это было очень полезно, только одно: это не работает на Android 6 (API 22). Затем следует изменить код на это: 'Build.VERSION.SDK_INT == 21' - Это как раз в' API 21' ** Я тестирую Nexus с API 22 ** – Saeid

+0

Тем не менее я не могу установить собственный шрифт, используя этот метод в Nexus 9 –

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

Вы можете установить пользовательские шрифты для каждого макета по одному, только с одним вызовом функции из каждого макета, передавая ее корень View.First, создать singelton подход для доступа к объекту шрифта, как этот

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

} 

вы можете определить различные шрифты в классе выше, определит класс Helper шрифта, который будет применять шрифты:

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 только можно применить шрифты на других элементах представления также аналогично. Вам просто нужно передать идентификатор вашей корневой группы View указанному выше методу шрифта. например, ваш макет:

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

В макете Выше корневой идентификатор родительского «Main Родитель» теперь позволяет применять шрифт

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

Ура :)

+0

Ницца .. не более одного и того же шрифта воссоздать, только 1 шрифт используется для всех полей.:) –

0

Я также хотел бы улучшить Ответ Weston для API 21 Android 5.0.

У меня была такая же проблема на моем Samsung s5 при использовании шрифта DEFAULT. (С другими шрифтами он работает отлично)

мне удалось сделать его работу, установив начертание («Санс», например) в файлах XML, для каждого TextView или кнопки

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

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

Надеюсь, это поможет.

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

Я хотел бы улучшить weston «s и Roger Huang» s ответы на более API 21 Android лол липоп с темой «Theme.AppCompat».

Ниже 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> 

Over (равный) 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 Util файл такой же, что в weston «ы ответ. я испытал в этих телефонах:

Nexus 5 (Android 5.1 Primary Android System)

ZTE V5 (Android 5.1 CM12.1)

Xiaomi нотой (Android 4.4 MIUI6)

HUAWEI C8850 (android 2.3.5 UNKNOWN)

7

Блестящее решение можно найти здесь: 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). Используемый код Weston и Roger Huang, а также https://stackoverflow.com/a/33236102/2914140. Также тестируется на 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); 
    } 
} 

Чтобы запустить код всего приложения вы должны написать в каком-то классе, как заявка на следующее:

new FontsOverride(this).loadFonts(); 

Создайте папку внутри «активов» «шрифтов» и поставить необходимые шрифты там. Простую инструкцию можно найти здесь: https://stackoverflow.com/a/31697103/2914140.

Устройство Lenovo также неправильно получает значение шрифта. В большинстве случаев он возвращает Typeface.NORMAL, иногда null. Даже если TextView выделен жирным шрифтом (в макете xml-файла). См. Здесь: TextView isBold always returns NORMAL. Таким образом, текст на экране всегда находится в обычном шрифте, не жирным или курсивом. Поэтому я думаю, что это ошибка производителя.

+0

Я изучил исходный код Android, ваш ответ лучший из лучших. Он может заменить тонкий, средний, легкий или что-нибудь еще. Спасибо огромное! – user2912553

+0

@ пользователь2912553, спасибо! – CoolMind

53

Существует большая библиотека для пользовательских шрифтов в андроиде: Calligraphy

вот пример, как использовать его.

В Gradle вам нужно поместить эту строку в сборку вашего приложения.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

Это идеальное решение. –

+1

Правда, это очень идеальное решение, поэтому мне не нужно писать разные классы, чтобы обновлять шрифты для пунктов меню, переключателя или флажка. Это относится ко всем !! спасибо :) – Manisha

+2

Определенно не идеальное решение: жирные/курсивные/полужирные шрифты перезаписываются. В настоящее время нет простого способа установить эти семьи. – 0101100101

28

В итоге:

Вариант # 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(); 
      } 
     } 
    } 

} 

Использование в классе Application:

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

Pre-леденец:

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

Option2: Подкласс каждый вид, в котором вам нужно настроить шрифт, то есть. ListView, EditTextView, кнопки и т.д. (Palani «s ответ):

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: Реализовать Просмотр Crawler, который проходит через иерархию зрения текущего экрана:

Вариант # 1 (ответ Tom «s):

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, так как он сэкономит много головных болей.

+0

В * Варианте # 1 *, что вы подразумеваете под '' sans-serif'' в 'newMap.put (' и 'monospace' в' styles'?! Я хочу использовать пользовательский шрифт с именем 'barana.ttf'. –

0

Calligraphy работает довольно хорошо, но он не подходит для меня, так как он не поддерживает различные веса (полужирный, курсив и т.д.) для шрифта-семьи.

Итак, я попробовал 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); 

    } 

} 
+0

Не идеальное решение. Для этого решения потребуются индивидуальные обходные пути для доступа к стандарту Android' TextView's – blueware

4

на Android O это теперь можно определить непосредственно из XML и my bug is now closed!

See here for details

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> 

Наслаждайтесь!

+0

. Спасибо. Мы должны использовать Android Studio 2.4 для возможности добавления папки шрифтов. –

+0

делает это работа для всего приложения? – SolidSnake

+0

На самом деле мне просто нужно применить его к моему стилю, и он будет применяться через приложение, но некоторые (программно добавленные элементы управления должны быть применены через Java-код) – SolidSnake

1

в апи 26 с build.gradle 3.0.0 вы можете создать каталог шрифтов в разрешении и использовать эту строку в стиле

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

для изменения build.gradle это в вашем build.gradle dependecies

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

и как вставить шрифт в проект – azerafati

+0

@azerafati скопируйте ваш шрифт в res/font –

+0

да, tanx. Я уже получил это. Но этот 'item' сам по себе не устанавливает t он шрифт для всего приложения. любой ключ? – azerafati

0

Наконец, Google понял всю серьезность этой проблемы (применения пользовательских шрифтов для компонентов пользовательского интерфейса), и они разработали чистое решение для него.

Во-первых, вам необходимо обновить библиотеку поддержки 26+ (возможно, вам также потребуется обновить студию gradle {4.0+}, android studio), затем вы можете создать новую папку ресурсов, называемую шрифтом. В этой папке вы можете разместить свои ресурсы шрифта (.tff, ...). Тогда вам необходимо изменить значение по умолчанию приложения их и заставить свой собственный шрифт в это :)

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

Примечания: если вы хотите, чтобы поддерживать устройства с более старым API, чем 16, вы должны использовать приложение имена вместо андроида!

0

Для изменения семейства шрифтов шрифтов TextViews переопределите textViewStyle в теме приложения.

Для использования пользовательского шрифта в fontFamily используйте ресурсы шрифта, которые находятся в библиотеке поддержки.

Функция была добавлена ​​в Android 26, но в более ранних версиях была добавлена ​​поддержка поддержки.

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

Смежные вопросы