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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • siege 用户登录_Siege详解[通俗易懂]

    siege 用户登录_Siege详解[通俗易懂]Siege是一款开源的压力测试工具,设计用于评估WEB应用在压力下的承受能力。可以根据配置对一个WEB站点进行多用户的并发访问,记录每个用户所有请求过程的相应时间,并在一定数量的并发访问下重复进行。Siege可以从您选择的预置列表中请求随机的URL。所以siege可用于仿真用户请求负载,而ab则不能。但不要使用siege来执行最高性能基准调校测试,这方面ab就准确很多。一、安装编译安装tar-z…

    2022年10月24日
    1
  • oracle恢复删除的数据时长_oracle 闪回查询语句

    oracle恢复删除的数据时长_oracle 闪回查询语句oracle提供了针对已经删除的数据恢复;分为两种方法:scn和时间戳两种方法恢复。

    2022年9月2日
    2
  • (1)美团面试题:Hashmap的结构,1.7和1.8有哪些区别,史上最深入的分析「建议收藏」

    (一)真实面试题之:Hashmap的结构,1.7和1.8有哪些区别不同点:(1)JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法,那么他们为什么要这样做呢?因为JDK1.7是用单链表进行的纵向延伸,当采用头插法就是能够提高插入的效率,但是也会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。(2)扩…

    2022年4月9日
    33
  • mips一个字是几个字节_计算机中字和字节的关系

    mips一个字是几个字节_计算机中字和字节的关系字节是byte,包括8个二进制位;字是word,长度与架构有关,如mips包括32个二进制位,一个字就是4个字节,它们的区别就是长度不一样。内存中是按字节寻值。因此是每byte也就是每8位存一个数值。当你想取一个word出来的时候,就是连续取了4个byte出来,然后拼成一个word。xx位机的xx位是指字长。这个字和word不一样,是指这种CPU一次…

    2022年10月1日
    0
  • 在线学习Java编程的最佳方法[通俗易懂]

    在线学习Java编程的最佳方法[通俗易懂]1.简介Java是使用最广泛的编程语言之一。根据Github的最新报告,Java被列为仅次于JavaScript的第二大最常用的编程语言。掌握Java的人有很多话题。好消息是,您可以在线找到很多内容。在本文中,我们将对所有这些主题进行分类,并提供对文章和代码示例的引用,这些文章和代码示例将指导您完成学习Java的过程。目录1.简介2.先决条件3.基础-核…

    2022年7月8日
    20
  • Windows 7(Win7)下Visual Studio 2012(VS2012)编译jrtplib与MinGW编译jrtplib

    一、下载jrtplib、jthread、CMake、VisualStudio2012和Qt5.5.1jrtplib:http://research.edm.uhasselt.be/jori/jrtplib/jrtplib-3.9.1.zipjthread:http://research.edm.uhasselt.be/jori/jthread/jthread-1.3.1.zipCM

    2022年4月8日
    46

发表回复

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

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