Android TextView setMaxLines后获取完整高度

     TextView设置完setMaxLines后,通过TextView.getHeight方法获取的是当前行数的高度,而非文字完全显示的高度。

     以下左边的图是《选择》这首诗一共只显示5行,右侧的图片中可以看到5行文字的高度,但是此时获取不到完整显示时TextView的高度

textView.setMaxLines(5);
textView.getHeight();   // 125

获取TextView的完整高度,核心代码

private int getTextViewHeight(TextView pTextView) {
    Layout layout = pTextView.getLayout();
    int desired = layout.getLineTop(pTextView.getLineCount());
    int padding = pTextView.getCompoundPaddingTop() + pTextView.getCompoundPaddingBottom();
    return desired + padding;
}

完整代码

public class MainActivity extends Activity {
    private static final String value = "选   择    汪国真\\n你的路\\n已经走了很长很长\\n走了很长"
            + "\\n可还是看不到风光\\n你的心很苦\\n很彷徨\\n没有风帆的船\\n不比死了强\\n没有罗盘的风帆"
            +"\\n只能四处去流浪\\n如果你是鱼不要迷恋天空\\n如果你是鸟不要痴情海洋";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView output = (TextView) findViewById(R.id.output);
        output.setText("在屏幕上点击,可以获取输出TextView高度信息");
        final TextView textView = (TextView) findViewById(R.id.textview);
        textView.setText( value );
        textView.setMaxLines(5);
        RelativeLayout layout = (RelativeLayout) findViewById(R.id.layout);
        layout.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append("当前TextView设置setMaxLines属性未全部显示\\n");
                stringBuilder.append("textView.getHeight = ");
                stringBuilder.append( textView.getHeight() );
                stringBuilder.append("\\n");
                stringBuilder.append("TextView全部文字显示的实际高度: ");
                stringBuilder.append(getTextViewHeight(textView));
                output.setText( stringBuilder.toString() );
                textView.setMaxLines(Integer.MAX_VALUE);
            }
        });
    }
    private int getTextViewHeight(TextView pTextView) {
        Layout layout = pTextView.getLayout();
        int desired = layout.getLineTop(pTextView.getLineCount());
        int padding = pTextView.getCompoundPaddingTop() + pTextView.getCompoundPaddingBottom();
        return desired + padding;
    }
}

   通过阅读TextView源码,查找其TextView宽高的计算方式,首先看TextView.onMeasure方法,TextView继承自View,View都是通过在onMeasure方法中确定宽高的。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 宽度相关代码不看,此处此关心与高度相关的
......
    // 高度
    if (heightMode == MeasureSpec.EXACTLY) {
        // Parent has told us how big to be. So be it.
        height = heightSize;
        mDesiredHeightAtMeasure = -1;
    } else {
         // 从这里获取的TextView高度
        int desired = getDesiredHeight();
        height = desired;
        mDesiredHeightAtMeasure = desired;
        if (heightMode == MeasureSpec.AT_MOST) {
            height = Math.min(desired, heightSize);
        }
    }
    // 没有填充的高度
    int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
    if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
        unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
    }
    /*
     * We didn't let makeNewLayout() register to bring the cursor into view,
     * so do it here if there is any possibility that it is needed.
     */
    if (mMovement != null ||
        mLayout.getWidth() > unpaddedWidth ||
        mLayout.getHeight() > unpaddedHeight) {
        registerForPreDraw();
    } else {
        scrollTo(0, 0);
    }
    setMeasuredDimension(width, height);
}

从上面代码中可以获取到,高度是通过此TextView.getDesiredHeight获得,接着查看这段代码

private int getDesiredHeight(Layout layout, boolean cap) {
    if (layout == null) {
        return 0;
    }
    // 行数
    int linecount = layout.getLineCount();
    // padding
    int pad = getCompoundPaddingTop() + getCompoundPaddingBottom();
    // 期望值
    int desired = layout.getLineTop(linecount);
    final Drawables dr = mDrawables;
    if (dr != null) {
        desired = Math.max(desired, dr.mDrawableHeightLeft);
        desired = Math.max(desired, dr.mDrawableHeightRight);
    }
    desired += pad;
    if (mMaxMode == LINES) {
        /*
         * Don't cap the hint to a certain number of lines.
         * (Do cap it, though, if we have a maximum pixel height.)
         */
        if (cap) {
            if (linecount > mMaximum) {
                desired = layout.getLineTop(mMaximum) +
                          layout.getBottomPadding();
                if (dr != null) {
                    desired = Math.max(desired, dr.mDrawableHeightLeft);
                    desired = Math.max(desired, dr.mDrawableHeightRight);
                }
                desired += pad;
                linecount = mMaximum;
            }
        }
    } else {
        desired = Math.min(desired, mMaximum);
    }
    if (mMinMode == LINES) {
        if (linecount < mMinimum) {
            desired += getLineHeight() * (mMinimum - linecount);
        }
    } else {
        desired = Math.max(desired, mMinimum);
    }
    // Check against our minimum height
    desired = Math.max(desired, getSuggestedMinimumHeight());
    return desired;
}