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的数值。这和代码中得到的结论相同。

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • [html]js无缝循环滚动图片示例代码

    [html]js无缝循环滚动图片示例代码html代码<!–轮播图–><divid=”banner”><ul><li><ahref=”YunNan.html”><imgsrc=”./微信图片_20200621003327.jpg”>1</a></li><li><ahref=”Switzerland.html”><imgsrc=”./微信图片_20200621003339.

  • max函数比较字符串类型

    max函数比较字符串类型

  • 变量放在堆还是栈_堆和栈的共同特点

    变量放在堆还是栈_堆和栈的共同特点内容会持续更新,有错误的地方欢迎指正,谢谢!什么是栈区,什么是堆区栈区(stack):由编译器自动分配释放,存放函数的参数值、局部变量的值等,内存的分配是连续的,类似于数组,当我们声明变量时,编译器会自动接着当前栈区的结尾来分配内存。堆区(heap):由程序员分配释放,若程序员不释放,程序结束时由OS回收,内存的分配不是连续的,类似于链表。变量存放的位置C/C++程…

  • java数组排序去重_JAVA数组去重排序

    java数组排序去重_JAVA数组去重排序今天也是很丧的一天,天气真的好热啊,胃不舒服,一直打嗝,中午吃了一碗凉掉的粥,然后开始拉肚子,一点不想要过来医院学习(基本上都是自己傻坐着,又没有老师来教我们,幽怨.jpg),但不来又过不去自己心里的那道坎,像是逃课的感觉,就很烦,然后在校门口纠结了十分钟还是过来了,emmmmm……然后就有了下面的代码packageArray;publicclassBubbleSort{publi…

  • 计算机测试英语词汇,英语听说测试-计算机专业英语词汇.pdf「建议收藏」

    英语听说测试-计算机专业英语词汇常用计算机专业英语1.filen.文件;v.保存文件43.enterv.键入,送入2.commandn.命令,指令44.marginn.余量,边缘,边际3.usev.使用,用途…

  • String类型转换BigDecimal、Date类型

    String类型转换BigDecimal、Date类型String类型转换BigDecimal类型public static void main(String[] args) {         String str1="2.30";            BigDecimal bd=new BigDecimal(str1);            System.out.println(bd);    }Java String类型转换成D…

发表回复

您的电子邮箱地址不会被公开。

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