Android TextView绘制之BoringLayout

前言

在Android开发中,TextView是一个很基础的控件。但看过它的源码你就会发现他其实一点都不简单。毕竟他也是不少view的父类。其中包含了很多布局、逻辑和计算。之前一直想有空就研究一下来着的。那么现在就开始吧。
这篇先讲TextView中三大布局(BoringLayout、StaticLayout、DynamicLayout)中最简单的BoringLayout。

BoringLayout

在官网中,BoringLayout的描述如下:

A BoringLayout is a very simple Layout implementation for text that fits on a single line and is all left-to-right characters. You will probably never want to make one of these yourself; if you do, be sure to call isBoring(CharSequence, TextPaint) first to make sure the text meets the criteria.

简单点说,BoringLayout是适用单行,文本方向为LTR(从左到右)的简单布局。不建议开发者自己适用,如果要用,需要先isBoring方法判断是否符合要求。

在TextView中,BoringLayout的创建入口是makeSingleLayout方法。

TextView#makeSingleLayout

protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,boolean useSaved) {
	Layout result = null;
    if (useDynamicLayout()) {
    	...
    }else {
        if (boring == UNKNOWN_BORING) {
            boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
            if (boring != null) {
                mBoring = boring;
            }
        }
        if (boring != null) {
            //如果测量宽度小于小想要的宽度,并且设置了ellipsize或者测量宽度小于省略后的长度
            if (boring.width <= wantWidth
                  && (effectiveEllipsize == null || boring.width <= ellipsisWidth)) {
                if (useSaved && mSavedLayout != null) {
                     result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint,
                                wantWidth, alignment, mSpacingMult, mSpacingAdd,
                                boring, mIncludePad);
                 } else {
                     result = BoringLayout.make(mTransformed, mTextPaint,
                                wantWidth, alignment, mSpacingMult, mSpacingAdd,
                                boring, mIncludePad);
                 }

                 if (useSaved) {
                     mSavedLayout = (BoringLayout) result;
                 }
            } else if (shouldEllipsize && boring.width <= wantWidth) {
                //如果需要处理省略号(如设置了singleLine),且测量宽度小于想要的宽度
                 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);
                  }
            }
       }
}

经过isBoring检测后符合要求,且满足宽度和设置省略条件,会创建BoringLayout,若之前创建过,useSaved为true,则通过replaceOrMake方法直接取用,否则通过make创建新的。在创建过程中,若设置了ellipsize省略,会调用TextUtils.ellipsize进行文本省略处理。

BoringLayout#isBoring

public static Metrics isBoring(CharSequence text, TextPaint paint,
            TextDirectionHeuristic textDir, Metrics metrics) {
        final int textLength = text.length();
    	//检测是否存在某些有趣的字符,如'\n','\t',
        //因为这些字符的存在会影响宽度计算和LTR方向
        if (hasAnyInterestingChars(text, textLength)) {
           return null;  // There are some interesting characters. Not boring.
        }
    	//检测文字方向,BoringLayout不支持从右到左。有些国家文字书写是从右到左
    	//关于字符分类和书写方向,有兴趣可以看https://www.sohu.com/a/348173901_298038
        if (textDir != null && textDir.isRtl(text, 0, textLength)) {
           return null;  // The heuristic considers the whole text RTL. Not boring.
        }
    	//如果是spanned类型,判断是否设置了样式,若设置了返回null
        if (text instanceof Spanned) {
            Spanned sp = (Spanned) text;
            Object[] styles = sp.getSpans(0, textLength, ParagraphStyle.class);
            if (styles.length > 0) {
                return null;  // There are some ParagraphStyle spans. Not boring.
            }
        }

        Metrics fm = metrics;
        if (fm == null) {
            fm = new Metrics();
        } else {
            fm.reset();
        }

    	//TextLine对文字进行测量
        TextLine line = TextLine.obtain();
        line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
                Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
                0,0 );
        fm.width = (int) Math.ceil(line.metrics(fm));
        TextLine.recycle(line);

        return fm;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KWMax

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值