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