请选择 进入手机版 | 继续访问电脑版

Android TextView绘制之过程分析

[复制链接]
谭先生 发表于 2021-1-1 18:33:11 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题


整体流程

onMeasure -> onLayout -> onDraw
onMeasure

  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int widthMode = MeasureSpec.getMode(widthMeasureSpec);    int heightMode = MeasureSpec.getMode(heightMeasureSpec);    int widthSize = MeasureSpec.getSize(widthMeasureSpec);    int heightSize = MeasureSpec.getSize(heightMeasureSpec);    ...    if (widthMode == MeasureSpec.EXACTLY) {        // 准确模式,以父容器给的宽度作当前TextView的宽度.        width = widthSize;    } else {        if (mLayout != null && mEllipsize == null) {            //调用desired方法盘算盼望值,如果行数大于1就返回-1,否则返回单行宽度            des = desired(mLayout);        }        //小于0,即行数大于1行,就去判定是否是boring        if (des < 0) {            boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);            if (boring != null) {               mBoring = boring;            }        } else {            fromexisting = true;        }        ...        //加上Drawable的宽度        final Drawables dr = mDrawables;        if (dr != null) {            width = Math.max(width, dr.mDrawableWidthTop);            width = Math.max(width, dr.mDrawableWidthBottom);        }        //盘算hint的宽度        if (mHint != null) {            ...        }        //加上padding边距        width += getCompoundPaddingLeft() + getCompoundPaddingRight();        ...        //最后根据上面盘算得到的size-padding的值就是我们单行text实际可以展示的大小        int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();        if (mLayout == null) {           makeNewLayout(want, hintWant, boring, hintBoring,                   width - getCompoundPaddingLeft() - getCompoundPaddingRight(),false);        }else {            ...        }        //接下来高度盘算,和上面的宽度盘算类似        if (heightMode == MeasureSpec.EXACTLY) {            // Parent has told us how big to be. So be it.            height = heightSize;            mDesiredHeightAtMeasure = -1;        } else {            //如果设置了Drawable的话,比力2个值得大小,取大的            //如果设置了maxLines或者maxHeight盘算下当前高度有没凌驾最大高度,凌驾的话取最大高度            //如果设置了minLines或者minHeight的话,比力下当前高度和最小高度,取小的            int desired = getDesiredHeight();            ...        }        ...    }}
复制代码
1、MeasureSpec是一个specMode和specSize信息的32位int值。此中高两位为测量模式specMode,低30位为测量值specSize。测量模式有UNSPECIFIED、EXACTLY、AT_MOST三种。一个View的测量模式由父容器和其自身的LayoutParams共同决定。
一般来说,View测量模式对应的情况如下
EXACTLY


  • View自己设置为固定值,比方20dp/px
  • 父容器为EXACTLY,View自己为match_paret
AT_MOST


  • 父容器为AT_MOST,View自己为wrap_content或match_parent
  • 父容器为EXACTLY,View自己为wrap_content
UNSPECIFIED


  • 父容器为UNSPECIFIED,View自己为wrap_content或match_parent
2、makeNewLayout为布局的创建入口,在这个方法中,主要举行赛马灯、省略号、HintLayout的处置惩罚。核心方法照旧调用makeSingleLayout。通过它,举行判定,选择创建BoringLayout、StaticLayout、DynamicLayout中的一种。详细创建过程可以看前面的文章。
onLayout

  1. @Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {    super.onLayout(changed, left, top, right, bottom);    if (mDeferScroll >= 0) {        int curs = mDeferScroll;        mDeferScroll = -1;        bringPointIntoView(Math.min(curs, mText.length()));    }    // Call auto-size after the width and height have been calculated.    autoSizeText();}
复制代码
layout过程,执行方法bringPointIntoView,根据前面onMeasure确定的各种LayoutParam参数,举行盘算确定在布局中的位置。autoSizeText会在设置了自动调治字体大小(mNeedsAutoSizeText为true)时,自动盘算和调治字体大小。
onDraw

TextView#onDraw方法中会有各种获取行数据信息的方法,它们就会访问的前面组织好的数据。
  1. @Overrideprotected void onDraw(Canvas canvas) {    final int compoundPaddingLeft = getCompoundPaddingLeft();    final int compoundPaddingTop = getCompoundPaddingTop();    final int compoundPaddingRight = getCompoundPaddingRight();    final int compoundPaddingBottom = getCompoundPaddingBottom();    ...    if (mEditor != null) {       mEditor.onDraw(canvas, layout, highlight, mHighlightPaint, cursorOffsetVertical);    } else {       layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);    }    ...}
复制代码
绘制之前,取出各个padding,确定绘制区域的范围和坐标。
若mEditor不为null,走Editor#onDraw方法,否则调用Layout#draw。跟踪Editor代码,可以知道onDraw照旧会调用Layout#draw或者直接在硬件加快状态下执行drawBackgrounddrawText方法。
Layoutdraw方法会执行drawBackgrounddrawText方法,前者绘制配景,后者调用TextLine对象,举行逐行绘制。
[code]public void draw(Canvas canvas, Path highlight, Paint highlightPaint,        int cursorOffsetVertical) {    final long lineRange = getLineRangeForDraw(canvas);    int firstLine = TextUtils.unpackRangeStartFromLong(lineRange);    int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);    if (lastLine < 0) return;    drawBackground(canvas, highlight, highlightPaint, cursorOffsetVertical,            firstLine, lastLine);    drawText(canvas, firstLine, lastLine);}public void drawText(Canvas canvas, int firstLine, int lastLine) {        ...    //遍历,一行行绘制    for (int lineNum = firstLine; lineNum
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )