美文网首页Android开发
TextView关于ellipsize二三事

TextView关于ellipsize二三事

作者: yo_ho_hoo | 来源:发表于2018-07-02 15:06 被阅读15次

1、引子

​ 今天碰到一个需求,显示一个字符串,最长显示6个字,超出部分显示省略号。我一看,这个简单,基本功能嘛,于是吭哧吭哧写了如下代码:

image

搞定收工!跑起来一看

image

我省略号呢?我那么大一个省略号呢?

image

难道长度不是用这个设置?许久不写代码手生了?接着吭哧吭哧一顿改:

image

结果。。。

image

还不如原来呢!!!(╯' - ')╯︵ ┻━┻

好吧,承认这个问题值得仔细斟酌了,对源码分析不感兴趣的童鞋可以直接到文末看结论。

2、分析

​ 首先看看什么时候会在TextView后面添加省略号,具体流程就不详细说了,只选取最基本的类型介绍。在makeSingleLayout中有这么一段

else if (shouldEllipsize && boring.width <= wantWidth) {
                    if (useSaved && mSavedLayout != null) {
                        result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint,
                                wantWidth, alignment, mSpacingMult, mSpacingAdd,
                                boring, mIncludePad, effectiveEllipsize,
                                ellipsisWidth);
                    } else {
                        result = BoringLayout.make(mTransformed, mTextPaint,
                                wantWidth, alignment, mSpacingMult, mSpacingAdd,
                                boring, mIncludePad, effectiveEllipsize,
                                ellipsisWidth);
                    }
                }
...
if (result == null) {
    StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
                    0, mTransformed.length(), mTextPaint, wantWidth)
                    .setAlignment(alignment)
                    .setTextDirection(mTextDir)
                    .setLineSpacing(mSpacingAdd, mSpacingMult)
                    .setIncludePad(mIncludePad)
                    .setBreakStrategy(mBreakStrategy)
                    .setHyphenationFrequency(mHyphenationFrequency);
    if (shouldEllipsize) {
                builder.setEllipsize(effectiveEllipsize)
                        .setEllipsizedWidth(ellipsisWidth)
                        .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
            }
}

​ 这里的shouldEllipsize标识主要根据TextView中的android:ellipsize属性是否被设置来判断。width的比较主要用于区别单行还是多行,也就是最后用BoringLayout来创建还是StaticLayout。我们选择BoringLayout分支继续往下跟。在mSavedLayout.replaceOrMake和BoringLayout.make这两个方法中,我们都能看到熟悉的代码

replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth,
                                           ellipsize, true, this),
                        paint, outerwidth, align, spacingmult,
                        spacingadd);

ellipsize方法介绍:

​ Returns the original text if it fits in the specified width given the properties of the specified Paint, or, if it does not fit, a truncated copy with ellipsis character added at the specified edge or center.

​ 返回一个原始的文本。如果这个文本适合指定的宽度及给定的属性(指定的画笔);否则,如果它不适合,一个截断的包括省略字符的复制会被添加到指定的边缘或者中心。

这里截取关键部分代码

MeasuredText mt = MeasuredText.obtain();
float width = setPara(mt, paint, text, 0, text.length(), textDir);
//如果字符宽度小于控件宽度,不需要省略号
if (width <= avail) {
    if (callback != null) {
        callback.ellipsized(0, 0);
    }

    return text;
}
// XXX assumes ellipsis string does not require shaping and
// is unaffected by style
float ellipsiswid = paint.measureText(ellipsis);
avail -= ellipsiswid;//这里是参数传入的允许宽度

int left = 0;
int right = len;
//根据ellipsize属性的值来计算
if (avail < 0) {
    // it all goes
} else if (where == TruncateAt.START) {
    right = len - mt.breakText(len, false, avail);//判断打断位置
} else if (where == TruncateAt.END || where == TruncateAt.END_SMALL) {
    left = mt.breakText(len, true, avail);
} else {
    right = len - mt.breakText(len, false, avail / 2);
    avail -= mt.measure(right, len);
    left = mt.breakText(right, true, avail);
}

if (callback != null) {
    callback.ellipsized(left, right);
}
...//省略代码
//拼装最后的Text
SpannableStringBuilder ssb = new SpannableStringBuilder();
            ssb.append(text, 0, left);
            ssb.append(ellipsis);
            ssb.append(text, right, len);
            return ssb;

​ 最后,得到了有省略号的Text,整个关于省略号拼装逻辑也就结束了。那么回到最开始的问题,到底什么时候会添加省略号呢?我们能看到,是否显示省略号最后是根据width <= avail这个判断来实现。width是根据文本内容计算出的文本宽度,avail是传入的控件宽度。那么结论很明显了,省略号是否显示,仅与控件宽度这一属性相关,和最大字符数和最大ems等属性并无关系。

3、结论

​ 那么,对于我的需求:显示一个字符串,最长显示6个字,超出部分显示省略号。怎么实现呢?1、写死控件宽度,让控件刚好满足6个字加上省略号的宽度。但是这个方法有个缺点,如果你按中文来定宽度,那么英文有可能显示超过6位。2、在给控件赋值前判断字符串长度,进行裁剪,并且在最后拼接省略号。

相关文章

网友评论

    本文标题:TextView关于ellipsize二三事

    本文链接:https://www.haomeiwen.com/subject/jdlmyftx.html