android字高度充满textview,TextView文字实际高度分析
问题
做Android开发的人都知道怎么设置文字高度:
1
2
3
4
5
6
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textSize="20sp"
android:text="Abc" />
可是有多少人注意到,在手机上这个TextView的实际高度是并不是20sp,为什么?
换句话说,如何使TextView文字的尺寸位置与设计稿精确一致?
分析
因手上只有魅蓝Note3(Flyme系统,基于Android 5.1,1090x1920,density=3),以下分析和结论均在此机子上测试,其他版本的原理都是一样的,区别在于字体文件和配置可能不一样。
(待续)
分析的过程就是各种查资料各种尝试,很多人其实只需要知道结论就行了,所以先把结论贴出来:
结论
1. 中文和英文放在同一个TextView会导致高度改变吗?NO
density=3屏幕下,”夔Madpx”/“s夔Madpx”/“Madpx”对应尺寸50sp/50dp/150px高度都是202px(includePadding)/176px
2. WRAP_CONTENT 的TextView的真实高度 与 textSize 是否成正比例?YES 无论是否includePadding,无论是否多行
3. 根据 textSize 如何确定单行 WRAP_CONTENT 的TextView的真实高度?
相关参数包括:
所使用字体(fallback的话不影响)的UPM(Units Per EM)
ascent/descent属性
top/bottom参数
禁止includePadding时, TextView实际占据高度是 (ascent - descent) / UPM * textSize
开启includePadding时, TextView实际占据高度是 (top - bottom) / UPM * textSize
4. includePadding的实际效果?
由于top/bottom对应于字体文件的所有图案的ymax和ymin,所以可以推出下面的结论:
如果设置了includePadding,则所设置字体里,最上和最下的图案能刚好容纳得下。
但是,如果发生了fallback,例如设置了英文字体(默认情况),但是出现了中文字符,绘制中文时通过fallback机制用了另一个字体文件的图案,那么,TextView的高度与另一个字体文件无关
所以,如果不指定字体文件(各种ROM的字体不一定一样),includePadding无法保证
includePadding上下空白的高度与top/bottom/ascent/descent有关,只影响第一行和最后一行
以下值基于fm = getPaint().fontMetrics
———— TopPadding = includePadding ? fm.top : fm.ascent
———— Ascent = fm.ascent
– Line1 – Baseline = 0
———— Descent = fm.descent
———— Ascent = fm.ascent
– Line2 – Baseline = 0
———— Descent = fm.descent
———— Ascent = fm.ascent
– Line3 – Baseline = 0
———— Descent = fm.descent
———— BottomPadding = includePadding ? fm.bottom : fm.descent
top和bottom分别对应字体文件参数里的ymax和ymin
top = font.props.ymax
bottom = font.props.ymin
所以
TopPadding = includePadding ? fm.top - fm.ascent: 0
BottomPadding = includePadding ? fm.bottom - fm.descent: 0
5. android中使用的是mac(hhea)的ascender/descender,还是OS/2的winAscent/winDescent,还是TypoAscender/TypoDescender?
从下面实验可以确定android使用的是mac(hhea)对应的ascender/descender
ascender/descender和winAscent/winDscent一致 字体DroidSansFallback-flyme-stub.ttf
E/FontSpace: textView3: space=172.851562 top=-121.875000, ascender=-121.875000, descender=18.164062, bottom=18.164062
把ascender/descender修改后的字体DroidSansFallback-flyme-stub-mod-mac.ttf
E/FontSpace: textView4: space=208.593750 top=-121.875000, ascender=-146.484375, descender=29.296875, bottom=18.164062
把winAscent/winDscent修改后的字体DroidSansFallback-flyme-stub-mod-win.ttf
E/FontSpace: textView5: space=172.851562 top=-121.875000, ascender=-121.875000, descender=18.164062, bottom=18.164062
把typoAscender/typoDscender修改后的字体DroidSansFallback-flyme-stub-mod-typo.ttf
E/FontSpace: textView6: space=172.851562 top=-121.875000, ascender=-121.875000, descender=18.164062, bottom=18.164062
6. TextView默认字体是哪个?
Flyme 5.1环境下,没有DroidSansFallback.ttf,不是DroidSansFallback-flyme.ttf,默认尺寸与设置了android:typeface=”sans”一致
从源代码TypefaceImpl.cpp,可以确认安卓5.1的默认字体是写死在代码里的,位置在/system/fonts/Roboto-Regular.ttf,与system_fonts.xml没什么关系,如果该文件加载异常,则默认字体是一个包含空列表List的FontFamily,会造成什么危害未知
1
2
3const char *fns[] = {
"/system/fonts/Roboto-Regular.ttf",
};
在Flyme5.1环境下,可以确认Roboto-Regular.ttf的fm与默认的fm一致
默认
E/FontSpace: textView1: space=175.781250 top=-159.228516, ascender=-139.160156, descender=36.621094, bottom=41.455078
指定Roboto-Regular.ttf字体
E/FontSpace: textView9: space=175.781250 top=-159.228516, ascender=-139.160156, descender=36.621094, bottom=41.455078
估计安卓系统的默认字体会考虑各种字体Fallback情况下,使用默认字体的上下留白依然不会造成截断情况,所以默认字体的上下留白会显得很宽
7. android的TextView如何渲染文本?通过Paint的getFontMetrics获取字体尺寸参数,主要逻辑在Paint.cpp和Skia库的SkPaint.cpp;对文本测量,确定分行处和补充
对文本测量,确定分行处和补充…的位置,创建StaticLayout/DynamicLayout/BoringLayout
Layout通过Canvas.drawText进行渲染
8. 如果发生Fallback,逻辑怎样?如果当前的字体文件找不到相应的图案,就会根据fonts.xml/fallback_fonts.xml的配置逐个检查直到找到为止(实现代码可能在Typeface/FontLoader)
如果找到新字体能提供该图案,则根据UPM和TextSize和Baseline确定绘制的位置和尺寸,但不影响TextView的测量高度
9. 如何使TextView的高度“恰好”(几乎)为中文或英文的高度?可以自定义只包含极少图案的字体,并设置合适的ascender/descender,如上面的DroidSansFallback-flyme-stub-mod-mac.ttf可以做到50dp的字体恰好WRAP_CONTENT的高度也是50dp
但这样就要针对特定版本(4.4/5.0/6.0)嵌入不同的字体,并且AOSP/Flyme等各个ROM的默认字体可能不一样
待续
10. 如何获取textView的fm和文字高度(space)?1
2
3TextPaint paint = textView.getPaint();
Paint.FontMetrics fm = new Paint.FontMetrics();
float space = paint.getFontMetrics(fm);
11. SourceHanSansCN-Normal.ttf和NotoSansHans-Regular.otf区别是?
从主要属性上来看,几乎没区别(数字差小于1%),前者字重是Light后者是Normal,但fonts设置的字重两者都是400。
前者有30888个图案,后者…
12. 假定思源字体fontSize = 100%,在不设置typeface/fontfamily的情况下,用的是什么字体?
英文使用Roboto-Regular.ttf绘制,中文会fallback到SourceHanSansCN-Normal.ttf->NotoSansHans-Regular.otf->DroidSansFallback(每个ROM可能不一样)
12.1 单行文字实际占用高度多少?
文字占用高度只跟默认字体有关,UPM=2048
禁止includePadding时, 实际占用高度为(1900 - -500)/2048 = 117.2%
开启includePadding时, 实际占用高度为(2174 - -566)/2048 = 133.8%
12.2 开启includePadding时上下各多占多少空白?
上面多(2174-1900)/2048 = 13.4%,下面多(566-500)/2048 = 3.2%
12.3 单行文字英文dp实际图案高度和上下空白多少?
ymax(d)=1547,ymin(p)=-427
实际占用高度为(1547 - -427)/2048 = 96.4%
禁止includePadding时,距离上边界(1900-1547)/2048=17.2%,距离下边界(-427 - -500)/2048=3.6%,如希望垂直方向精确居中,需补偿下边距17.2%-3.6%=13.6%
开启includePadding时,距离上边界(2174-1547)/2048=30.6%,距离下边界(-427 - -566)/2048=6.8%,如希望垂直方向精确居中,需补偿下边距30.6%-6.8%=23.8%
12.4 单行文字英文M实际图案高度和上下空白多少?
ymax(M)=1467,ymin(M)=-11
实际占用高度为(1467 - -11)/2048 = 72.2%
禁止includePadding时,距离上边界(1900-1467)/2048=21.1%,距离下边界(-11 - -500)/2048=23.9%,如希望垂直方向精确居中,需补偿上边距23.9-21.1%=2.8%
开启includePadding时,距离上边界(2174-1467)/2048=34.5%,距离下边界(-11 - -566)/2048=27.1%,如希望垂直方向精确居中,需补偿下边距34.5%-27.1%=7.4%
12.5 单行文字中文实际图案高度和上下空白多少?
只考虑常用中文情况下,fallback生效的字体是思源字体,因为两者区别极小,所以以SourceHanSansCN-Normal.ttf的值来计算,UPM=1000
常用中文字符的ymax和ymin都是字符’夔’(\u5914),ymax(夔)=842,ymin(夔)=-74
实际占用高度为(842 - -74)/1000 = 91.6%
禁止includePadding时,距离上边界1900/2048-842/1000=8.6%,距离下边界-74/1000 - -500/2048=17.0%,如希望垂直方向精确居中,需补偿上边距17.0-8.6%=8.4%
开启includePadding时,距离上边界2174/2048-842/1000=22.0%,距离下边界-74/1000 - -566/2048=20.2%,如希望垂直方向精确居中,需补偿下边距22.0%-20.2%=1.8%
所以,如果想指定偏大的高度值让英文垂直居中,禁止includePadding更居中,想让中文垂直居中,开启includePadding更居中
12.6 多行中文设置多大的lineExtraSpace才能实现n倍行距?
默认字体中文的高度填充率是91.2%,所以打算实现n倍行距需要补充的额外空白 = n * 91.2 - (100 - 91.2) = 0.912n - 0.088
13. 如何测量设计稿的文字尺寸?
PS/画图上设置的字号跟实际的文字高度不一样,实际高度跟字号、抗锯齿和文字效果有关,但没找到明显规律,想精确还原设计稿,直接测量字符高度就好。
android字高度充满textview,TextView文字实际高度分析相关推荐
- Android中自定义Textview解决文字和数字换行不整齐
效果图: 上面的是原生Textview,第一行末尾数字整体换行了, 下面是自定义Textview,第一行末尾数字分别在第一行和第二行展示 布局中 MyTextview记得换成自己包名下的 <T ...
- Android 实现TextView后面跟随一个高度和宽度固定的ImageView
安卓实现TextView后面跟随一个高度和宽度固定的ImageView(不要问我为什么不直接用drawableRight,因为宽高不好控制)TextView只为1行,即singleLine为true, ...
- android textView 替文字添加下划线 删除线
android textView 替文字添加下划线 删除线 方法1: tv=(TextView)findViewById(R.id.tv); tv.getPaint().setFlags(Paint. ...
- Android TextView 设置文字背景色或文字颜色,字体阴影,字体样式
String str="这是设置TextView部分文字背景颜色和前景颜色的demo!"; int bstart=str.indexOf("背 ...
- android textview表情,Android开发(16)-TextView显示表情图像和文字
从这个案例中我们可以学到当我们美化图片美化界面的时候可以在某一区域输入图片和文字混搭信息,第三张图片按比例缩小,第四张图像有超链接 布局文件 MainActivity.java package com ...
- android textview基线,Textview画文字基线的问题
有些小伙伴在自定义View的时候需要画文字,当画文字的基线的时候出现不少问题, 调用 canvas.drawText(text, x , y , paint); 这里的text字体,x开始的位置,y基 ...
- Android 原生控件之一 TextView
Android 原生控件之一 TextView 前言 来源 开始 XML属性 1.android:allowUndo 2.android:autoLink 3.android:autoSizeMaxT ...
- Android中设置显示文本,Android文本显示控件-TextView属性详解
android:autoLink //设置是否当文本为URL链接/email/电话号码/map时,文本显示为可点击的链接.可选值(none/web /email/phone/map/all) andr ...
- TextView设置文字竖着排放
TextView设置文字竖着排放:很简单,只要设置宽高大小自适应,每行最多显示长度为1即可! <TextViewandroid:layout_width="wrap_content&q ...
最新文章
- python 乘法运算定律_计算机组成原理(上)资料
- 虚拟机无法开机数据恢复 (建议在做之前做测试,数据双重备份)
- tomcat如何部署.net程序_.NET 程序员如何学习Vue
- 前端学习(3263):js中undefine
- .Net中TextBox对于焦点的控制(二)
- 第四季-专题12-按键驱动程序设计
- 屏幕取色:画板和ColorPix总结
- java编码用gbk还是utf-8_utf-8还是GBK、java的编码问题
- 盘姬工具箱WV1.10
- 数据分析~中国五大城市PM2.5数据分析01
- JavaScript|日期格式化、今天、昨天、明天和某天
- Windows远程桌面实现物理机访问控制虚拟机2-Tomcat发布网页
- JS 异步编程的解决方案,以及回调地狱的解决方案
- win10 如何查看redis版本
- Java版电商购物系统说明
- Win10打补丁KB4022725出现0x80073712错误
- 舆情监测系统功能简介,网络舆情监测系统平台有哪些?
- [物理学与PDEs]第4章习题1 反应力学方程组形式的化约 - 动量方程与未燃流体质量平衡方程...
- 转载 各大流行linux版本评价
- Win10下安装必应输入法能导致Alt+Tab切换页面时不能置于最前