半行文字显示“查看更多”TailTextView

有一个需求

当一个文本过长的时候会显示很多行,为了显示更多的信息(露出更多的view),会出现查看更多的需求。类似于下图:


半行显示查看更多

有一个想法

因为是在TextView内部处理这个事,所以一定要自定义TextView,把新的TextView叫做TailTextView。
大致思路如下:

  • 先使用maxLines属性将TextView的理想高度测量出来
  • 找到最后一行字母的位置,加上查看更多,再加个颜色,调用一下回调方法
  • 重新使用setText,将新的CharSequence设置进去

做一个实现

自定义属性写了两个:showTail(是否显示小尾巴)和tailColor(尾巴颜色)
重写onMeasure方法,先使用super的测量方法得到TextView每一行的布局
拿到TextView前maxLines-1行的文字,作为一定显示的文字,最后一行作为要处理的文字
测量出小尾巴的宽度,将一行最大的宽度减去小尾巴宽度,得到原来文字最大宽度,使用这个宽度对最后一行文字进行裁切。
在刚才得到的文字上加上“…查看更多”,设置span,回调被裁切方法。
将新的Text重新设置到TextView上。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //不显示小尾巴或者已经处理过的不再处理
        if (!showTail || mEllipsizeFinal) {
            return;
        }
        int lineCount = getLineCount();
        Layout layout = getLayout();
        int maxLines = getMaxLines();
        //行数没有到maxLines不处理
        if (maxLines == 0 || lineCount < maxLines || TextUtils.isEmpty(getText())){
            return;
        }
        //一共的文字数量
        int totalChars = layout.getLineEnd(maxLines - 1);
        int lastLineStartIndex = layout.getLineStart(maxLines - 1);

        if (totalChars >= getText().length()) {
            return;
        }
        CharSequence mustShowText = getText().subSequence(0, lastLineStartIndex);

        String tailText = "…查看全文";
        float tailWidth = getPaint().measureText(tailText);
        CharSequence lastLineText;
        int screenWidth = DeviceUtils.getScreenWidth(getContext());
        //最后一个字是个换行符就把这个换行符去掉,不然不能在那一行后面增加文字了
        if (LINE_BREAKER.equals(String.valueOf(getText().charAt(totalChars - 1)))) {
            lastLineText = getText().subSequence(lastLineStartIndex, totalChars - 1);
        } else {
            lastLineText = getText().subSequence(lastLineStartIndex, totalChars);
        }
        //这里可能会出现每一行都有换行符,然后整个TextView的宽度很小的情况
        //对可能超过右边进行修正,screenWidth * 0.6f是应该可以调整的
        float maxWidth = Math.max(screenWidth * 0.6f, getMeasuredWidth());
        CharSequence ellipsizeLastLineText = TextUtils.ellipsize(lastLineText, getPaint(), maxWidth - tailWidth,
            TextUtils.TruncateAt.END);
        if (ellipsizeLastLineText.length() > 2 && ellipsizeLastLineText != lastLineText) {
            lastLineText = ellipsizeLastLineText.subSequence(0, ellipsizeLastLineText.length() - 1);
        }

        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(mustShowText);
        spannableStringBuilder.append(lastLineText);
        int start = spannableStringBuilder.length();
        spannableStringBuilder.append(tailText);
        spannableStringBuilder.setSpan(new ForegroundColorSpan(tailColor), start + 1, start + 5,
            Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        //重新设置文本
        super.setText(spannableStringBuilder);
        //每设置一次text要重置一个这个位。重写setText(在里面重新置位)
        mEllipsizeFinal = true;
        onEllipsize();//回调是否被裁切
    }

效果就是上面放的图了。

写一些Tips

设置maxLines属性之后就不要设置ellipsize="end"了,可能会引起最后一行的宽度测量不准确。
screenWidth * 0.6f有点草率,可以修改的
如果要使用原来的maxLines属性的功能就设置showTail为false
没有给出TailTextView的全文,但核心内容就是这些了

推荐阅读更多精彩内容