Android TextView行间距解析

Android TextView行间距解析介绍Android中TextView的行间距设置方法,并分析每项设置对间距的影响。

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

TextView行间距设置

在布局XML中有两个参数可以对TextView的行间距进行设置。
分别为:android:lineSpacingExtra 和 android:lineSpacingMultiplier。
在代码中可以通过TextView的setLineSpacing()方法来设置。

android:lineSpacingExtra

android:lineSpacingExtra表示额外的行间距数值,单位通常为dp。如android:lineSpacingExtra=”1dp”。
android:lineSpacingExtra的值可以为负数,小数和0。如果值为正数表示增加行间距,如果值为负数表示减少行间距,如果值为0,则没有变化。

android:lineSpacingMultiplier

android:lineSpacingMultiplier表示行间距的倍数,没有单位。如android:lineSpacingMultiplier=”1.2”。
android:lineSpacingMultiplier的值可以为任意浮点数。如果值大于1.0表示增加行间距,如果值小于1.0表示减少行间距。

android:lineSpacingExtra和android:lineSpacingMultiplier一起使用

android:lineSpacingExtra和android:lineSpacingMultiplier可以在一起对同一个TextView进行设置,同时使用时会先增加android:lineSpacingMultiplier设置的倍数,再加上android:lineSpacingExtra设置的额外的间距。

setLineSpacing()

setLineSpacing()原型为public void setLineSpacing(float add, float mult);
参数add表示要增加的间距数值,对应android:lineSpacingExtra参数。
参数mult表示要增加的间距倍数,对应android:lineSpacingMultiplier参数。

不同行间距设置下的显示效果

设置不同的android:lineSpacingExtra显示效果如下。

正常行间距下显示效果
正常行间距

设置android:lineSpacingExtra=”2dp”时的显示效果
2dp行间距

设置android:lineSpacingExtra=”-6dp”时的显示效果
-6dp行间距

设置android:lineSpacingExtra=”-10dp”时的显示效果
-10dp行间距

设置不同的android:lineSpacingMultiplier显示效果如下。
正常行间距下显示效果
正常行间距

设置android:lineSpacingMultiplier=”1.2”时的显示效果
1.2倍行间距

设置android:lineSpacingMultiplier=”2.0”时的显示效果
2.0倍行间距

设置android:lineSpacingMultiplier=”0.5”时的显示效果
0.5倍行间距

设置android:lineSpacingMultiplier=”0”时的显示效果(等于0时文字不可见)
0倍行间距

行间距设置影响分析

对正常行间距,android:lineSpacingExtra=”2dp”,和android:lineSpacingMultiplier=”2”设置下的实际行间距进行测量,得到如下数值
正常行间距下,实际间距为12px
正常行间距

+2dp行间距下,实际间距为18px
+2dp行间距

2倍行间距下,实际间距为64px
2倍行间距

可以看到在android:lineSpacingExtra=”2dp”设置下,实际行间距也增加了6px(测试手机上1dp=3px),但在android:lineSpacingMultiplier=”2”设置下,实际行间距并不是正常行间距的两倍。
进一步的,如果在测量时将文字高度也计算在内,得到如下数值:
正常行间距下,包含文字高度间距为52px
正常行间距

+2dp行间距下,包含文字高度间距为58px
+2dp行间距

2倍行间距下,包含文字高度间距为104px(这里测量使用的是第二行的间距,原因下文会描述)
2倍行间距

通常意义下所说的行间距是指一行文字下方距离下一行文字上方的距离,行高是指一行文字上方距离下一行文字上方的距离。
通过对比可以看出,android:lineSpacingExtra和android:lineSpacingMultiplier的设置并不是直接影响行间距,而是通过行高来影响行间距。当设置android:lineSpacingExtra=”2dp”时,行高增加2dp,文字高度不变,于是行间距增加2dp。当设置android:lineSpacingMultiplier=”2”时,行高增加两倍,文字高度不变,行间距增加(”文字高度”+”文字正常行间距”)

获取TextView行高

TextView提供了getLineHeight()方法来获取TextView的行高。
对正常行间距,android:lineSpacingExtra=”2dp”和android:lineSpacingMultiplier=”2”设置下通过getLineHeight()方法获取的行高如下。
正常行间距:52px
android:lineSpacingExtra=”2dp”:58px
android:lineSpacingMultiplier=”2”:104px
和实际测量值相同。

TextView行高和间距的异常情况

TextView实际显示的每行高度并不总是和getLineHeight()方法获取到的行高相等(这也意味着每行的间距也并不总是等于getLineHeight()方法获取到的行高减去文字高度)。
TextView getLineHeight()方法的注释如下

    /**
     * @return the height of one standard line in pixels.  **Note that markup
     * within the text can cause individual lines to be taller or shorter
     * than this height, and the layout may contain additional first-
     * or last-line padding.**
     */

Jetbrains全家桶1年46,售后保障稳定

也就是说如果某行文字内有markup(不知道是什么),那么该行文字高度可能会大于,也可能会小于getLineHeight()方法返回的高度。此外对TextView的首行和最后一行有一个额外的padding间距,这导致实际行高要大于getLineHeight()方法得到的行高,实际间距也要大于通过getLineHeight()计算得到的间距。
对首行行高实际测量得到如下数值。
android:lineSpacingMultiplier=”1”时,“首行行高=普通行行高”
android:lineSpacingMultiplier=”2”时,“首行行高=普通行行高+6px”
android:lineSpacingMultiplier=”3”时,“首行行高=普通行行高+12px”
android:lineSpacingMultiplier=”4”时,“首行行高=普通行行高+18px”
也就是说“首行行高=普通行行高 + 6 ×(android:lineSpacingMultiplier – 1)”

TextView源码分析

要了解为何android:lineSpacingExtra和android:lineSpacingMultiplier设置影响的是行高而不是行间距,以及为何首行和最后一行的行高和普通行不相等,需要通过TextView源码来进行分析。
TextView 构造函数部分源码和setLineSpacing()源码如下

    case com.android.internal.R.styleable.TextView_lineSpacingExtra:
        mSpacingAdd = a.getDimensionPixelSize(attr, (int) mSpacingAdd);
        break;
    case com.android.internal.R.styleable.TextView_lineSpacingMultiplier:
        mSpacingMult = a.getFloat(attr, mSpacingMult);
        break;
public void setLineSpacing(float add, float mult) {
        if (mSpacingAdd != add || mSpacingMult != mult) {
            mSpacingAdd = add;
            mSpacingMult = mult;

            if (mLayout != null) {
                nullLayouts();
                requestLayout();
                invalidate();
            }
        }
    }

即android:lineSpacingExtra的设置保存在mSpacingAdd成员变量中,android:lineSpacingMultiplier的设置保存在mSpacingMult成员变量中。

TextView getLineHeight()源码如下

    public int getLineHeight() {
        return FastMath.round(mTextPaint.getFontMetricsInt(null) * mSpacingMult + mSpacingAdd);
    }

可以看到TextView计算行高就是将正常高度乘以mSpacingMult再加上mSpacingAdd得到的。
跟踪mSpacingAdd和mSpacingMult成员变量,发现在makeSingleLayout()方法中调用new StaticLayout构造方法时使用了这两个变量。在StaticLayout类的构造方法中调用了generate()方法,在generate()方法中又调用了out()方法,在out()方法中可以看到如下代码

if (needMultiply && !lastLine) {
    double ex = (below - above) * (spacingmult - 1) + spacingadd;
    if (ex >= 0) {
        extra = (int)(ex + EXTRA_ROUNDING);
    } else {
        extra = -(int)(-ex + EXTRA_ROUNDING);
    }
} else {
    extra = 0;
}

其中needMultiply值定义为,即设置了android:lineSpacingExtra和android:lineSpacingMultiplier中任意一个,needMultiply的值就为true。

boolean needMultiply = (spacingmult != 1 || spacingadd != 0);

那么只要设置了android:lineSpacingExtra和android:lineSpacingMultiplier中任意一个,就会计算一个extra的值,这个extra的值等于(below – above) * (spacingmult – 1) + spacingadd向上取整后的值。
在这之后有如下代码

lines[off + TOP] = v;
...
v += (below - above) + extra;
...
lines[off + mColumns + TOP] = v;

跟踪lines[]相关代码可以看到,lines[]是mLines的引用,而mLines中保存了每行文字的垂直位置信息,每个普通行文字有三个位置信息,分别对应mLines中的三个元素(mColumns = COLUMNS_NORMAL = 3, START = 0, TOP = 1, DESCENT = 2)。
因此lines[off + mColumns + TOP]表示的是下一行的TOP位置信息,将lines[off + mColumns + TOP] – lines[off + TOP]就可以得到下一行的TOP位置距离本行的TOP位置距离,也就是行高。显然lines[off + mColumns + TOP] – lines[off + TOP] = (below – above) + extra。如果忽略extra计算时的取整过程,则lines[off + mColumns + TOP] – lines[off + TOP] = (below – above) + extra = (below – above) + (below – above) * (spacingmult – 1) + spacingadd = (below – above) * spacingmult + spacingadd。这就解释了为何android:lineSpacingExtra和android:lineSpacingMultiplier设置影响的是行高而不是行间距。
在out()方法往前可以看到如下代码。

if (firstLine) {
    if (trackPad) {
        mTopPadding = top - above;
    }

    if (includePad) {
        above = top;
    }
}

跟踪includePad变量,可以看到是TextView的mIncludePad变量值,而TextView中mIncludePad固定设置为true,因此这里above = top;将执行,也就是说对首行文字,其above位置等于top位置,由此可以得出lines[off + mColumns + TOP] – lines[off + TOP] = (below – above) * spacingmult + spacingadd = (below – top) * spacingmult + spacingadd。也就是说首行文字的行高要比普通行高出(top – above) * spacingmult个像素。
注意到above = top的设定,首行文字在测量其行高时,其起始位置应当在文字top – above像素上方的空白位置。结合上文中首行行高的测量情况有如下数值。
android:lineSpacingMultiplier=”1”时,“首行行高=普通行行高+6px”
android:lineSpacingMultiplier=”2”时,“首行行高=普通行行高+12px”
android:lineSpacingMultiplier=”3”时,“首行行高=普通行行高+18px”
android:lineSpacingMultiplier=”4”时,“首行行高=普通行行高+24px”
也就是说“首行行高 – 普通行行高 = 6 ×(android:lineSpacingMultiplier)”这里的6显然就是top – above的数值。这和代码中得到的结论相同。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210056.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 使用 Amazon EC2 启动 Windows 虚拟机「建议收藏」

    使用 Amazon EC2 启动 Windows 虚拟机

    2022年2月15日
    40
  • 最经典的黑客入门教程[通俗易懂]

    最经典的黑客入门教程[通俗易懂]第一节、黑客的种类和行为以我的理解,“黑客”大体上应该分为“正”、“邪”两类,正派黑客依靠自己掌握的知识帮助系统管理员找出系统中的漏洞并加以完善,而邪派黑客则是通过各种黑客技能对系统进行攻击、入侵或者做其他一些有害于网络的事情,因为邪派黑客所从事的事情违背了《黑客守则》,所以他们真正的名字叫“骇客”(Cracker)而非“黑客”(Hacker),也就是我们平时经常听说的“黑客”(Cacker)和“红客”(Hacker)。无论那类黑客,他们最初的学习内容都将是本部分所涉及的内容,而且掌握的基本技能也都是.

    2022年6月7日
    34
  • Intellij IDEA打开Java项目并启动「建议收藏」

    Intellij IDEA打开Java项目并启动「建议收藏」最近有很多同学,竟然不知道如何使用IntellijIDEA打开Java项目并启动目录Maven项目2、Maven项目是以pom文件引入各项jar包的在点击lmportProject,然后在点击pom.xml,再点击Open​3、再点击Next​4、如果是公司的私服,还需要勾选nexus​5、继续点击下一步​6、选择Jdk版本,点击下一步​7、点击Finish​……

    2022年7月8日
    182
  • java-version未找到命令_org.apache.catalina.core.stand

    java-version未找到命令_org.apache.catalina.core.standspringboot2.x工程,将jasypt升级到3.x–>3.0.3后,配置一个属性为加密,工程启动后报错如下############################################################

    2022年9月26日
    3
  • pstack 与 strace[通俗易懂]

    pstack 与 strace[通俗易懂]strace  strace是可以记录程序系统调用的一个工具,它可以记录该程序系统调用的各个延时情况。-c统计每一系统调用的所执行的时间,次数和出错的次数等.-d输出strace关于标准错误的调试信息.-f跟踪由fork调用所产生的子进程.-ff如果提供-ofilename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.-F尝试跟踪vf…

    2022年9月14日
    0
  • 树莓派连接WiFi(最稳定的方法)[通俗易懂]

    树莓派连接WiFi(最稳定的方法)[通俗易懂]1概述树莓派是一个只有信用卡大小的卡片式电脑,基于ARM架构,采用Linux作为其操作系统;它默认是通过有线接口连接互联网,对于如此小巧的设备,有线连接非常不方便,下面我们介绍下如何让树莓派通过无线网卡连接网络。网上大多数文章介绍的是编辑  /etc/network/interfaces  文件,修改成如下的形式:ifacewlan0inetdhcpwpa-

    2022年5月22日
    64

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号