TextView关于ellipsize二三事

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、在给控件赋值前判断字符串长度,进行裁剪,并且在最后拼接省略号。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容