Использовать шрифт Roboto для более ранних устройств
Я хотел бы использовать шрифт Roboto в своем приложении для Android и убедиться, что он работает для более ранних версий Android, у которых нет установленного шрифта. Я знаю, что могу сделать это, используя Typeface.createFromAsset(), а затем вручную установив шрифт для каждого из моих TextViews/Buttons/Other-Objects. Это кажется большой болью для этого для каждого объекта, который я показываю на экране, хотя.
Мой вопрос в том, есть ли лучший способ сделать это? Какой-то вспомогательный класс или способ установить пользовательский шрифт в файле темы .xml? Любое автоматическое будет лучше, чем вручную перечислять каждый объект на каждом экране и изменять шрифт.
Спасибо!
Ответы
Ответ 1
Извлеките все виды изнутри, проверьте его тип и примените соответствующие действия.
Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Roboto/Roboto-Regular.ttf");
for (View view : allViews)
{
if (view instanceof TextView)
{
TextView textView = (TextView) view;
textView.setTypeface(typeface);
}
}
Ответ 2
Вышеприведенный ответ верен, но я просто хотел реализовать свою реализацию здесь
Мой класс утилиты:
package com.example.utils;
import android.content.Context;
import android.graphics.Typeface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class AndroidUtils
{
private static Typeface robotoTypeFace;
public static void setRobotoFont (Context context, View view)
{
if (robotoTypeFace == null)
{
robotoTypeFace = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto/Roboto-Regular.ttf");
}
setFont(view, robotoTypeFace);
}
private static void setFont (View view, Typeface robotoTypeFace)
{
if (view instanceof ViewGroup)
{
for (int i = 0; i < ((ViewGroup)view).getChildCount(); i++)
{
setFont(((ViewGroup)view).getChildAt(i), robotoTypeFace);
}
}
else if (view instanceof TextView)
{
((TextView) view).setTypeface(robotoTypeFace);
}
}
}
Как использовать его, если this
- это Activity:
AndroidUtils.setRobotoFont(this, view);
Чтобы установить тот же шрифт для всего TextView, вы можете использовать decorView своей деятельности:
ViewGroup godfatherView = (ViewGroup)this.getWindow().getDecorView();
AndroidUtils.setRobotoFont(this, godfatherView);
Если у вас есть адаптеры или фрагменты, не забудьте также установить их шрифт.
Смотрите здесь.
Ответ 3
Благодаря @Jitsu, @Arnaud и @Pawan M, я сделал свое решение, лучше, чем все из них:
/**
* Adapted from http://stackoverflow.com/a/12387343/450148
*
* @author Anton Averin
* @author Felipe Micaroni Lalli
*/
package net.alouw.alouwCheckin.util;
import android.content.Context;
import android.graphics.Typeface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.EnumMap;
import java.util.Map;
public final class FontUtils {
private FontUtils() {
}
private enum FontType {
BOLD("fonts/Roboto/Roboto-BoldCondensed.ttf"),
BOLD_ITALIC("fonts/Roboto/Roboto-BoldCondensedItalic.ttf"),
NORMAL("fonts/Roboto/Roboto-Condensed.ttf"),
ITALIC("fonts/Roboto/Roboto-CondensedItalic.ttf");
private final String path;
FontType(String path) {
this.path = path;
}
public String getPath() {
return path;
}
}
/* cache for loaded Roboto typefaces*/
private static Map<FontType, Typeface> typefaceCache = new EnumMap<FontType, Typeface>(FontType.class);
/**
* Creates Roboto typeface and puts it into cache
*/
private static Typeface getRobotoTypeface(Context context, FontType fontType) {
String fontPath = fontType.getPath();
if (!typefaceCache.containsKey(fontType)) {
typefaceCache.put(fontType, Typeface.createFromAsset(context.getAssets(), fontPath));
}
return typefaceCache.get(fontType);
}
/**
* Gets roboto typeface according to passed typeface style settings.
* <p/>
* Will get Roboto-Bold for Typeface.BOLD etc
*/
private static Typeface getRobotoTypeface(Context context, Typeface originalTypeface) {
FontType robotoFontType = null;
if (originalTypeface == null) {
robotoFontType = FontType.NORMAL;
} else {
int style = originalTypeface.getStyle();
switch (style) {
case Typeface.BOLD:
robotoFontType = FontType.BOLD;
break;
case Typeface.BOLD_ITALIC:
robotoFontType = FontType.BOLD_ITALIC;
break;
case Typeface.ITALIC:
robotoFontType = FontType.ITALIC;
break;
case Typeface.NORMAL:
robotoFontType = FontType.NORMAL;
break;
}
}
return (robotoFontType == null) ? originalTypeface : getRobotoTypeface(context, robotoFontType);
}
/**
* Walks ViewGroups, finds TextViews and applies Typefaces taking styling in consideration
*
* @param context - to reach assets
* @param view - root view to apply typeface to
*/
public static void setRobotoFont(Context context, View view) {
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
setRobotoFont(context, ((ViewGroup) view).getChildAt(i));
}
} else if (view instanceof TextView) {
Typeface currentTypeface = ((TextView) view).getTypeface();
((TextView) view).setTypeface(getRobotoTypeface(context, currentTypeface));
}
}
}
И последнее, что в вашей основной деятельности onCreate:
if (Build.VERSION.SDK_INT < 11) {
ViewGroup godfatherView = (ViewGroup) this.getWindow().getDecorView();
FontUtils.setRobotoFont(this, godfatherView);
}
И в моих списках с пользовательскими представлениями код выше не работал, поэтому мне пришлось сделать это:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// (...)
View view = // build your custom view here
if (Build.VERSION.SDK_INT < 11) {
FontUtils.setRobotoFont(activity, view);
}
return view;
}
Ответ 4
Вот моя улучшенная версия этого решения.
Он может кэшировать шрифты и принимать во внимание параметры параметра TextView.textStyle. Таким образом, он может выделять жирный текст.
http://anton.averin.pro/2012/09/12/how-to-use-android-roboto-font-in-honeycomb-and-earlier-versions/
Ответ 5
Я использую другое решение. Я установил пользовательскую LayoutInflater.Factory для активности. Поэтому у меня есть полный доступ к просмотру после его создания. Я могу установить пользовательский шрифт для каждого TextView без итерации по иерархии представлений. Одна вещь, которую вы должны сделать, чтобы использовать пользовательский шрифт во всем своем приложении, - это вызов new Font(...).install()
в вашем базовом действии. См. Ниже приведенный ниже.
Вот мое решение с образцом использования:
import java.util.Map;
import com.google.common.collect.Maps;
import static com.google.common.base.Preconditions.checkNotNull;
import android.R;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.support.v4.app.FragmentActivity;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
/**
* Provides an ability to apply custom font to all {@link TextView} and subclasses.
*
* To install custom font use method {@link #install(Activity)} in {@link Activity#onCreate(android.os.Bundle)}
* <b>before</b> calling super.onCreate(Bundle).
*
* <p/>Example of usage:
* <pre>
* {@code
* public class BaseActivity extends SherlockFragmentActivity {
*
* protected void onCreate(Bundle state) {
* applyCustomFontForPreICS();
* super.onCreate(state);
* }
*
* private void applyCustomFontForPreICS() {
* boolean isPreICS = Build.VERSION.SDK_INT < BUILD_VERSION_CODE_ICE_CREAM_SANDWICH
* if (isPreICS) {
* new Font(
* "font/roboto_regular.ttf",
* "font/roboto_bold.ttf",
* "font/roboto_italic.ttf",
* "font/roboto_bold_italic.ttf"
* ).install(this);
* }
* }
* }
* }
* </pre>
*
* @author Alexey Danilov ([email protected])
*/
public class Font {
private static final Map<String, Typeface> FONTS = Maps.newHashMap();
private String regularFontPath;
private String boldFontPath;
private String italicFontPath;
private String boldItalicFontPath;
/**
* Creates instance to be used for setting particular font.
*
* @param regularPath regular font assets path, must be not {@code null}
* @param boldPath bold font assets path, must be not {@code null}
* @param italicPath italic font assets path, must be not {@code null}
* @param boldItalicPath bold and italic font assets path, must be not {@code null}
*/
public Font(String regularPath, String boldPath, String italicPath, String boldItalicPath) {
this.regularFontPath = checkNotNull(regularPath);
this.boldFontPath = checkNotNull(boldPath);
this.italicFontPath = checkNotNull(italicPath);
this.boldItalicFontPath = checkNotNull(boldItalicPath);
}
/**
* Installs custom font to activity.
*
* @param activity an activity custom font will be installed to, must be not {@code null}.
*/
public void install(Activity activity) {
checkNotNull(activity, "Activity must be not null!");
LayoutInflater layoutInflater = activity.getLayoutInflater();
boolean factoryIsEmpty = layoutInflater.getFactory() == null;
if (!factoryIsEmpty) {
throw new IllegalStateException("Impossible to use this method for this activity: layout factory is set!");
}
layoutInflater.setFactory(new FontLayoutInflaterFactory());
}
private Typeface getFont(int type, Context context) {
switch (type) {
case Typeface.NORMAL:
return getFont(context, regularFontPath);
case Typeface.BOLD:
return getFont(context, boldFontPath);
case Typeface.ITALIC:
return getFont(context, italicFontPath);
case Typeface.BOLD_ITALIC:
return getFont(context, boldItalicFontPath);
default: {
throw new IllegalArgumentException("Undefined font type " + type);
}
}
}
private Typeface getFont(Context context, String path) {
if (FONTS.containsKey(path)) {
return FONTS.get(path);
} else {
Typeface typeface = makeTypeface(context, path);
FONTS.put(path, typeface);
return typeface;
}
}
private Typeface makeTypeface(Context context, String path) {
try {
return Typeface.createFromAsset(context.getAssets(), path);
} catch (Exception e) {
// add user-friendly error message
throw new IllegalArgumentException(String.format("Error creating font from assets path '%s'", path), e);
}
}
private void applyFontToTextView(Context context, TextView textView, AttributeSet attrs) {
int[] fontStyleAttributes = {R.attr.textStyle};
TypedArray typedArray = context.obtainStyledAttributes(attrs, fontStyleAttributes);
boolean isStyleSpecified = typedArray.getIndexCount() != 0;
int type = isStyleSpecified ? typedArray.getInt(0, Typeface.NORMAL) : Typeface.NORMAL;
Typeface font = getFont(type, context);
textView.setTypeface(font, type);
}
private final class FontLayoutInflaterFactory implements LayoutInflater.Factory {
// to improve perfomance the package with the most usable components should be the first.
private final String[] ANDROID_UI_COMPONENT_PACKAGES = {
"android.widget.",
"android.webkit.",
"android.view."
};
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
try {
// we install custom LayoutInflater.Factory, so FragmentActivity have no chance set own factory and
// inflate tag <fragment> in method onCreateView. So call it explicitly.
if ("fragment".equals(name) && context instanceof FragmentActivity) {
FragmentActivity fragmentActivity = (FragmentActivity) context;
return fragmentActivity.onCreateView(name, context, attrs);
}
View view = createView(name, attrs, LayoutInflater.from(context));
if (view == null) {
// It strange! The view is not ours neither android's. May be the package of this view
// is not listed in ANDROID_UI_COMPONENT_PACKAGES. Return null for the default behavior.
Log.d(LOG_TAG, "Cannot create view with name: " + name);
return null;
}
if (view instanceof TextView) {
TextView textView = (TextView) view;
applyFontToTextView(context, textView, attrs);
}
return view;
} catch (InflateException e) {
Log.e(LOG_TAG, "Error inflating view", e);
return null;
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG, "Error inflating view", e);
return null;
}
}
private View createView(String name, AttributeSet attrs, LayoutInflater layoutInflater) throws ClassNotFoundException {
View view = null;
boolean isAndroidComponent = name.indexOf('.') == -1;
if (isAndroidComponent) {
// We don't know package name of the view with the given simple name. Try android ui packages listed in
// ANDROID_UI_COMPONENT_PACKAGES
// The same implementation is in the class PhoneLayoutInflater from internal API
for (String androidPackage : ANDROID_UI_COMPONENT_PACKAGES) {
try {
view = layoutInflater.createView(name, androidPackage, attrs);
if (view != null) {
break;
}
} catch (ClassNotFoundException e) {
// Do nothing, we will try another package
}
}
} else {
view = layoutInflater.createView(name, null, attrs);
}
return view;
}
}
}
Обратите внимание, что он имеет зависимость от guava, но вы можете реализовать эти методы самостоятельно.
Ответ 6
Лучшее решение всегда Calligraphy