美文网首页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