Android TextView基于语言切换字体:中文自定义/英文默认的通用方法咨询
实现Android TextView中文用自定义字体、英文回退系统默认的方案
当然有可行的方案!我在实际项目里处理过类似需求,给你分享两种最常用的实现方式:
方案一:自定义TextView(推荐,复用性拉满)
核心思路是在自定义TextView里自动处理文本,给中文部分应用自定义字体,英文部分保留系统默认样式,以后所有需要这个效果的地方直接用这个自定义View就行:
- 先把你的自定义中文字体文件(比如
custom_zh.ttf)放到assets/fonts/目录下,记得创建对应的文件夹哦。 - 写一个继承自
TextView的自定义类,在构造方法里加载字体,并重写setText方法处理文本:
public class ZhEnTextView extends TextView { private Typeface mCustomZhTypeface; public ZhEnTextView(Context context) { super(context); init(context); } public ZhEnTextView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public ZhEnTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { // 加载自定义中文字体 mCustomZhTypeface = Typeface.createFromAsset(context.getAssets(), "fonts/custom_zh.ttf"); } @Override public void setText(CharSequence text, BufferType type) { if (text == null) { super.setText(text, type); return; } // 用SpannableString给中文单独设置字体 SpannableString spannable = new SpannableString(text); // 遍历每个字符,判断是否为中文字符 for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); // 用UnicodeScript判断是最准确的,能覆盖简体、繁体等各类汉字 if (Character.UnicodeScript.of(c) == Character.UnicodeScript.HAN) { spannable.setSpan(new CustomTypefaceSpan(mCustomZhTypeface), i, i+1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } super.setText(spannable, type); } }
- 因为系统自带的
TypefaceSpan只能用系统内置字体,所以我们要自己写个CustomTypefaceSpan类来支持自定义字体:
public class CustomTypefaceSpan extends MetricAffectingSpan { private final Typeface mTypeface; public CustomTypefaceSpan(Typeface typeface) { mTypeface = typeface; } @Override public void updateDrawState(TextPaint ds) { applyCustomTypeface(ds, mTypeface); } @Override public void updateMeasureState(TextPaint paint) { applyCustomTypeface(paint, mTypeface); } private static void applyCustomTypeface(Paint paint, Typeface typeface) { Typeface oldTypeface = paint.getTypeface(); int oldStyle = oldTypeface != null ? oldTypeface.getStyle() : 0; int fakeStyle = oldStyle & ~typeface.getStyle(); // 保留原文本的加粗、斜体样式 if ((fakeStyle & Typeface.BOLD) != 0) { paint.setFakeBoldText(true); } if ((fakeStyle & Typeface.ITALIC) != 0) { paint.setTextSkewX(-0.25f); } paint.setTypeface(typeface); } }
- 最后在布局里直接用这个自定义TextView就行,和普通TextView用法完全一样:
<com.yourpackage.ZhEnTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello 你好 World 世界" />
方案二:手动用SpannableString处理(适合单次文本场景)
如果只是某个TextView需要这个效果,不想自定义View,也可以直接在代码里手动处理:
TextView textView = findViewById(R.id.tv_test); // 加载自定义字体 Typeface customZhTypeface = Typeface.createFromAsset(getAssets(), "fonts/custom_zh.ttf"); String targetText = "Hello 你好 World 世界"; SpannableString spannable = new SpannableString(targetText); // 遍历字符设置Span for (int i = 0; i < targetText.length(); i++) { char c = targetText.charAt(i); if (Character.UnicodeScript.of(c) == Character.UnicodeScript.HAN) { spannable.setSpan(new CustomTypefaceSpan(customZhTypeface), i, i+1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } textView.setText(spannable);
同样需要用到上面的CustomTypefaceSpan类哦。
一些小提醒
- 字符判断准确性:用
Character.UnicodeScript.of(c)判断中文字符比单纯判断Unicode范围更靠谱,能覆盖各类汉字(包括繁体、日文汉字等,如果你不需要可以再调整判断逻辑)。 - 字体文件优化:如果你的自定义字体只包含中文字符,文件大小会小很多,加载速度更快,也能避免英文被自定义字体意外覆盖(不过上面的方案已经手动处理了,所以即使字体带英文也没关系)。
- 性能问题:哪怕文本很长,遍历字符的开销几乎可以忽略,Android的Spannable机制效率很高,不用担心卡顿。
内容的提问来源于stack exchange,提问作者Antony Ng
相关产品推荐
相关产品推荐

