Android开发系列:TextView绘制(三)DynamicLayout

DynamicLayout

官网中,DynamicLayout描述如下

DynamicLayout is a text layout that updates itself as the text is edited.

This is used by widgets to control text layout. You should not need to use this class directly unless you are implementing your own widget or custom display object, or need to call Canvas.drawText() directly.

DynamicLayout是一个编辑时文本会更新的布局。

根据前两篇文章,关于BoringLayout和StaticLayout,我们知道在TextView#makeSingleLayout方法中进行了布局的生成。DynamicLayout也是。

protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,boolean useSaved) {
	if (useDynamicLayout()) {
            final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,
                    wantWidth)
                    .setDisplayText(mTransformed)
                    .setAlignment(alignment)
                    .setTextDirection(mTextDir)
                    .setLineSpacing(mSpacingAdd, mSpacingMult)
                    .setIncludePad(mIncludePad)
                    .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
                    .setBreakStrategy(mBreakStrategy)
                    .setHyphenationFrequency(mHyphenationFrequency)
                    .setJustificationMode(mJustificationMode)
                    .setEllipsize(getKeyListener() == null ? effectiveEllipsize : null)
                    .setEllipsizedWidth(ellipsisWidth);
            result = builder.build();
     }else {
     	...
     }
     ...
}

和StaticLayout一样,DynamicLayout也使用Builder模式创建。

Builder会先初始化一些变量。其中,mText为原始文本,mTransformed为待展示的文本。例如,在EditText中(EditText为编辑属性的TextView子类),设置了密码规则,则mTransformed就会把文本设置为圆点。

在build方法中完成DynamicLayout初始化。

public DynamicLayout build() {
    final DynamicLayout result = new DynamicLayout(this);
    Builder.recycle(this);
    return result;
}

private DynamicLayout(@NonNull Builder b) {
        super(createEllipsizer(b.mEllipsize, b.mDisplay),
            b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd);
        mDisplay = b.mDisplay;
        mIncludePad = b.mIncludePad;
        mBreakStrategy = b.mBreakStrategy;
        mJustificationMode = b.mJustificationMode;
        mHyphenationFrequency = b.mHyphenationFrequency;

        generate(b);
}

private static CharSequence createEllipsizer(@Nullable TextUtils.TruncateAt ellipsize,
            @NonNull CharSequence display) {
        if (ellipsize == null) {
            return display;
        } else if (display instanceof Spanned) {
            return new SpannedEllipsizer(display);
        } else {
            return new Ellipsizer(display);
        }
}

可以看到,初始化先处理省略号设置。这个和StaticLayout类似。

主要的初始化过程在generate方法中完成。

private void generate(@NonNull Builder b) {
	mBase = b.mBase;
    mFallbackLineSpacing = b.mFallbackLineSpacing;
    if (b.mEllipsize != null) {
        mInts = new PackedIntVector(COLUMNS_ELLIPSIZE);
        ...
    }else {
        mInts = new PackedIntVector(COLUMNS_NORMAL);
        ...
    }
    mObjects = new PackedObjectVector<>(1);
    ...
    mInts.insertAt(0, start);
    ...
    mInts.insertAt(1, start);
    mObjects.insertAt(0, dirs);
    ...
    reflow(mBase, 0, 0, baseLength);
    ...
}

初始化两个Vector对象,它们的作用和StaticLayout中的mLines类似,内部会用数组保存一些Span的信息。

接着调用reflow方法,从文本开始,找到真实有效文字的信息,保存在上面两个对象中。主要是从后往前及从前往后根据换行符**‘\n’**来确定位置。

public void reflow(CharSequence s, int where, int before, int after) {
	...
	int find = TextUtils.lastIndexOf(text, '\n', where - 1);
	...
    int look = TextUtils.indexOf(text, '\n', where + after);
    ...
}

reflow方法中,还会用到StaticLayout,用它来获取这些文字的布局信息,比如需要多少行,如果发现有空行的话,就会从mIntsmObjects中删除对应位置的数据。

拿到具体需要几行显示后,就会遍历每一行,获取每一行的信息,存储在mIntsmObjects中。

之后会通过updateBlocks,将文本分块,每个Block的长度是400字符,多余的会被放到新的Block中。

updateBlocks判断数组是否已经创建,如果没有,就创建新的Block,否则就对之前的进行调整。

public void updateBlocks(int startLine, int endLine, int newLineCount) {
	if (mBlockEndLines == null) {
        createBlocks();
        return;
    }
    ...
}

private void createBlocks() {
        int offset = BLOCK_MINIMUM_CHARACTER_LENGTH;
        mNumberOfBlocks = 0;
        final CharSequence text = mDisplay;

        while (true) {
            offset = TextUtils.indexOf(text, '\n', offset);
            //找到换行符就添加换行符之前的,没有就全部文本
            if (offset < 0) {
                addBlockAtOffset(text.length());
                break;
            } else {
                addBlockAtOffset(offset);
                offset += BLOCK_MINIMUM_CHARACTER_LENGTH;
            }
        }
        // 创建一个索引数组
        mBlockIndices = new int[mBlockEndLines.length];
        for (int i = 0; i < mBlockEndLines.length; i++) {
            mBlockIndices[i] = INVALID_BLOCK_INDEX;
        }
}

当这些数据组织完成后,最终还会通过LayoutonDraw方法,绘制到界面上。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

KWMax

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

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

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

打赏作者

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

抵扣说明:

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

余额充值