【Android】TextView中不同大小字体如何上下垂直居中?
来源:互联网 发布:毛利寿三郎 越知月光 编辑:程序博客网 时间:2024/06/04 23:32
前言
在客户端开发中,我们往往需要对一个TextView的文字的部分内容进行特殊化处理,比如加粗、改变颜色、加链接、下划线等。iOS为我们提供了AttributedString
,而Android则提供了SpannableString
。
在Android的android.text.style包下为我们提供了各种各样的span(可以参考这篇文章),例如:
AbsoluteSizeSpan(int size) —— 设置字体大小,参数是绝对数值,相当于Word中的字体大小
RelativeSizeSpan(float proportion) —— 设置字体大小,参数是相对于默认字体大小的倍数,比如默认字体大小是x, 那么设置后的字体大小就是x*proportion,这个用起来比较灵活,proportion>1就是放大(zoom in), proportion<1就是缩小(zoom out)
BackgroundColorSpan(int color) —— 背景着色,参数是颜色数值,可以直接使用android.graphics.Color里面定义的常量,或是用Color.rgb(int, int, int)
ForegroundColorSpan(int color) —— 前景着色,也就是字的着色,参数与背景着色一致
问题
网上已经有着很多使用这些span的教程了,所以没必要在这里继续探讨这些基础使用了。但是,如果使用了AbsoluteSizeSpan(int size)
在同一个TextView中定义了不同字体大小,就会默认显示成底部对齐的方式:
说到这里,第一反应肯定是tv.setGravity(Gravity.CENTER_VERTICAL)
,但是很不幸,怎么试都不凑效。那么到底有没有办法使用Span让不同字体大小的垂直居中呢?
答案是:当然可以,得用ReplacementSpan
分析
为何是ReplacementSpan?
它是系统提供给我们的一个抽象类。通过名字我们可以知道其实用于是用于替换。指示我们可以把文本的某一部分替换成我们想要的内容。这也许是我们想要的。
Relpacement
的定义很简单:
public abstract class ReplacementSpan extends MetricAffectingSpan { public abstract int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm); public abstract void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint); public void updateMeasureState(TextPaint p) { } public void updateDrawState(TextPaint ds) { }}
我们在继承它的时候,需要实现两个方法getSize()
和draw()
。通过方法名,我们也许能够知道其作用:getSize()
用于确定span的大小(实际上只是一个宽度),draw()
用于绘制我们想要的内容。
但是问题来了,这些方法的传参是什么?为何getSize()
只返回了一个int值?
了解了这两个问题,就基本弄懂了自定义span。来回答这两个问题前,我们首先要明确的一件事情是:span是用于SpannableString
中,并且最终被用于TextView
中。所以在定义span时,我们的大小、绘制内容都应该依赖于使用时的环境。我们假设自定义span使用的环境为A,那么A将包换一些信息,例如:baseline
、Paint
、FontMetricsInt
等信息。
那我们现在来看看getSize()
方法。getSize()
的返回值是int,其实这个值指的是自定义span的宽度,那它的高度呢?其实高度是已知的,那就是外界环境A带来的字的高度。但我某些情况我们希望改变span的高度,我们该怎么做呢? 如果对Android上字体绘制有一定了解的同学会知道,一个字的高度取决于绘制这个子的Paint.FontMetricsInt
什么是 Paint.FontMetrics
它表示绘制字体时的度量标准。google的官方api文档对它的字段说明如下:
其中:
- ascent : 字体最上端到基线的距离,为负值。
- descent:字体最下端到基线的距离,为正值。
如上图,中间那条线(Baseline)就是基线,基线到上面那条线的距离就是ascent
,基线到下面那条线的距离就是descent
。
回到我们的主题, 我们发现getSize()
方法的参数中有Paint.FontMetricsInt
,那我们是否就可以通过改变传入的Paint.FontMetricsInt的asent
和desent
来达到改变高度的目的呢?答案是可行的。
解决方法
按照上面的分析,我们继承ReplacementSpan
自定义一个Span
/** * 使TextView中不同大小字体垂直居中 */public class CustomVerticalCenterSpan extends ReplacementSpan { private int fontSizeSp; //字体大小sp public CustomVerticalCenterSpan(int fontSizeSp){ this.fontSizeSp = fontSizeSp; } @Override public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { text = text.subSequence(start, end); Paint p = getCustomTextPaint(paint); return (int) p.measureText(text.toString()); } @Override public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { text = text.subSequence(start, end); Paint p = getCustomTextPaint(paint); Paint.FontMetricsInt fm = p.getFontMetricsInt(); canvas.drawText(text.toString(), x, y - ((y + fm.descent + y + fm.ascent) / 2 - (bottom + top) / 2), p); //此处重新计算y坐标,使字体居中 } private TextPaint getCustomTextPaint(Paint srcPaint) { TextPaint paint = new TextPaint(srcPaint); paint.setTextSize(ViewUtils.getSpPixel(mContext, fontSizeSp)); //设定字体大小, sp转换为px return paint; }}
解释下形参:
- x:要绘制的image的左边框到textview左边框的距离。
- y:要替换的文字的基线(Baseline)的纵坐标。
- top:替换行的最顶部位置。
- bottom:替换行的最底部位置。注意,textview中两行之间的行间距是属于上一行的,所以这里bottom是指行间隔的底部位置。
- paint:画笔,包含了要绘制字体的度量信息。
所以就有:
y + fm.descent
:得到字体的descent
线坐标;y + fm.ascent
:得到字体的ascent
线坐标;
(y + fm.descent + y + fm.ascent) / 2
也就是字体中间线的纵坐标
((y + fm.descent + y + fm.ascent) / 2 - (bottom + top) / 2)
就是字体需要向上调整的距离
使用方式
SpannableString ss = new SpannableString(disStr + unitString);ss.setSpan(new AbsoluteSizeSpan(40, true), 0, disStr.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);//垂直居中显示文字ss.setSpan(new CustomVerticalCenterSpan(23), disStr.length(), ss.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
看看效果:
【参考资料】
How to make RelativeSizeSpan align to top?
How to create vertically aligned superscript and subscript in TextView
教你自定义android中span
Android ImageSpan与TextView中的text居中对齐问题解决(无论TextView设置行距与否)
- 【Android】TextView中不同大小字体如何上下垂直居中?
- android TextView控件如何解决一个文本中出现不同大小的字体
- Android的TextView中显示不同大小字体的玩法
- Android中TextView文字居中与垂直靠左居中
- TextView显示不同大小字体
- Android中的一个TextView中的字体设置不同大小
- Android中的一个TextView中的字体设置不同大小
- android 通过一Textview设置不同大小的字体颜色
- Android中的一个TextView中的字体设置不同大小
- Android中的一个TextView中的字体设置不同大小
- Android-垂直上下滚动的TextView
- 一起学android之如何设置TextView中不同字段的字体颜色(22)
- Android中如何给TextView添加下划线、设置不同字体和颜色
- css---flex布局中,如何响应式 得水平垂直居中?flex子元素左右 上下居中
- Android中对同一个TextView设置不同字体样式
- html如何让table表格垂直(上下)居中
- Android中TextView如何让文字垂直显示
- Android中TextView如何实现水平和垂直滚动
- char * const *(*next)();
- How to access Package Name from JNI/NDK?
- 控制台光标(二):设置光标位置
- js获取html标签
- 使用Angular2及WebApi开发SPA类型的企业应用 - Part 3 项目结构
- 【Android】TextView中不同大小字体如何上下垂直居中?
- Android 仿iOS 开关SwitchButton
- CListCtrl高亮显示一行
- js闭包-个人浅理解
- HDU 3395 Special Fish(KM算法)
- LeetCode笔记:260. Single Number III
- java专有名词解释
- MyEclipse的可视化创建Hibernate项目
- 深入理解7816(3)-----关于T=0