Android 官方Typeface:Droid sans, Droid Sans Mono, Droid Serif, Default sans
TextStyle: normal, bload, italic
常用Spannable类
字体颜色:ForegroundColorSpan
字体背景颜色:BackgroundColorSpan
字体大小:AbsoluteSizeSpan
粗体,斜体:StyleSpan
删除线:StrikeThroughSpan
下划线:UnderlineSpan
图片:ImageSpan
字体:TypefaceSpan
自定义字体:class CustomTypefaceSpan extends TypefaceSpan
使用方法
SpannableString spanString = new SpannableString("xxxxx"); StyleSpan span = new StyleSpan(Typeface.BOLD_ITALIC); spanString.setSpan(span, 1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); textView.setText(spanString);
|
setTypeface
设置自定义字体
TextView txt = (TextView) findViewById(R.id.custom_font); Typeface font = Typeface.createFromAsset(getAssets(), "Chantelli_Antiqua.ttf"); txt.setTypeface(font);
|
CustomeTypefaceSpan
设置自定义字体
使用
Typeface typeface = Typeface.createFromAsset(getAssets(), "Akshar.ttf"); SpannableString str = new SpannableString("hello world"); str.setSpan(new CustomTypefaceSpan("", typeface), 0, string.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
CustomeTypefaceSpan.java
public class CustomTypefaceSpan extends TypefaceSpan {
private final Typeface newType;
public CustomTypefaceSpan(String family, Typeface type) { super(family); newType = type; }
@Override public void updateDrawState(TextPaint ds) { applyCustomTypeFace(ds, newType); }
@Override public void updateMeasureState(TextPaint paint) { applyCustomTypeFace(paint, newType); }
private static void applyCustomTypeFace(Paint paint, Typeface tf) { int oldStyle; Typeface old = paint.getTypeface(); if (old == null) { oldStyle = 0; } else { oldStyle = old.getStyle(); }
int fake = oldStyle & ~tf.getStyle(); if ((fake & Typeface.BOLD) != 0) { paint.setFakeBoldText(true); }
if ((fake & Typeface.ITALIC) != 0) { paint.setTextSkewX(-0.25f); }
paint.setTypeface(tf); } }
|
How set Spannable object font with custom font
自定义字体可能导致的问题
- Ellipsizing might not work correctly if the font dosen’t have a glyph for ellipsis character(没有省略字符的话导致肾略效果失效)
- internationalization might not be supported(可能对国际化,多语言不支持)
- 增加了apk的体积
老版本的new TypeFace()会存在内存泄漏,此bug在Android 3.2上已fixed
处理内存泄漏fix方案:
public class FontCache {
private static Hashtable<String, Typeface> fontCache = new Hashtable<String, Typeface>();
public static Typeface get(String name, Context context) { Typeface tf = fontCache.get(name); if(tf == null) { try { tf = Typeface.createFromAsset(context.getAssets(), name); } catch (Exception e) { return null; } fontCache.put(name, tf); } return tf; } }
|
http://stackoverflow.com/questions/16901930/memory-leaks-with-custom-font-for-set-custom-font/16902532#16902532
https://code.google.com/p/android/issues/detail?id=9904
Typeface源码
Typeface.create()方法返回的Typeface是存在缓存的
public static Typeface create(String familyName, int style) { if (sSystemFontMap != null) { return create(sSystemFontMap.get(familyName), style); } return null; }orm public static Typeface create(Typeface family, int style) { Typeface typeface; SparseArray<Typeface> styles = sTypefaceCache.get(ni);
if (styles != null) { typeface = styles.get(style); if (typeface != null) { return typeface; } } }
|
但是public static Typeface createFromAsset(AssetManager mgr, String path)
创建的Typeface好像不存在缓存,反正代码是没看懂
Does Typeface.createFromAsset() cache?
测试加载一个8M的字体文件耗时100ms,200kb的字体文件20ms
默认的情况下String.format()传入的SpannableString会当做普通的String处理,不会保留SpannableString中字体,样式等特性
public class SpanFormatter { public static final Pattern FORMAT_SEQUENCE = Pattern.compile("%([0-9]+\\$|<?)([^a-zA-z%]*)([[a-zA-Z%]&&[^tT]]|[tT][a-zA-Z])"); private SpanFormatter(){} public static SpannedString format(CharSequence format, Object... args) { return format(Locale.getDefault(), format, args); } public static SpannedString format(Locale locale, CharSequence format, Object... args){ SpannableStringBuilder out = new SpannableStringBuilder(format); int i = 0; int argAt = -1; while (i < out.length()){ Matcher m = FORMAT_SEQUENCE.matcher(out); if (!m.find(i)) break; i=m.start(); int exprEnd = m.end(); String argTerm = m.group(1); String modTerm = m.group(2); String typeTerm = m.group(3); CharSequence cookedArg; if (typeTerm.equals("%")){ cookedArg = "%"; }else if (typeTerm.equals("n")){ cookedArg = "\n"; }else{ int argIdx = 0; if (argTerm.equals("")) argIdx = ++argAt; else if (argTerm.equals("<")) argIdx = argAt; else argIdx = Integer.parseInt(argTerm.substring(0, argTerm.length() - 1)) -1; Object argItem = args[argIdx]; if (typeTerm.equals("s") && argItem instanceof Spanned){ cookedArg = (Spanned) argItem; }else{ cookedArg = String.format(locale, "%"+modTerm+typeTerm, argItem); } } out.replace(i, exprEnd, cookedArg); i += cookedArg.length(); } return new SpannedString(out); } }
|
参考
Combining Spannable with String.format()
a version of String.format