TextView.setText几个相关的面试题

263 阅读2分钟

Android面试的几个问题

1 TextView如何优化?

2 给TextView设置新的字符串,整个布局会重新绘制吗?

3 requestLayout和invalidate方法有什么区别?

TextView如何优化

联想到View的性能优化,一般都是想办法减少measure,layout,draw方法的时间,但是对TextView的优化一般都是给TextView设置固定的宽高,这样在TextView文本发生变化的时候就只会重新走TextView的draw方法,没有重新measure和layout,达到了性能优化的目的。

下面我们直接从源码分析:

private void setText(CharSequence text, BufferType type,
                     boolean notifyBefore, int oldlen) {
    //...前面省略
    if (mLayout != null) {
        checkForRelayout();
    }

    sendOnTextChanged(text, 0, oldlen, textLength);
    onTextChanged(text, 0, oldlen, textLength);

    if (a11yTextChangeType == AccessibilityUtils.TEXT) {
        notifyViewAccessibilityStateChangedIfNeeded(
                AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
    } else if (a11yTextChangeType == AccessibilityUtils.PARCELABLE_SPAN) {
        notifyViewAccessibilityStateChangedIfNeeded(
                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
    }

    if (needEditableForNotification) {
        sendAfterTextChanged((Editable) text);
    } else {
        notifyListeningManagersAfterTextChanged();
    }

    if (mEditor != null) {
        // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
        mEditor.prepareCursorControllers();

        mEditor.maybeFireScheduledRestartInputForSetText();
    }
}

这里主要看checkForRelayout()这个方法,继续看

private void checkForRelayout() {
    // If we have a fixed width, we can just swap in a new text layout
    // if the text height stays the same or if the view height is fixed.

    if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT
            || (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
            && (mHint == null || mHintLayout != null)
            && (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
        // Static width, so try making a new text layout.

        int oldht = mLayout.getHeight();
        int want = mLayout.getWidth();
        int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();

        /*
         * No need to bring the text into view, since the size is not
         * changing (unless we do the requestLayout(), in which case it
         * will happen at measure).
         */
        makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
                      mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
                      false);
        //不是跑马灯样式的文本
        if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
            // In a fixed-height view, so use our new text layout.
            //高度是一个精确值比如:android:layout_height="100dp" 
            // 这里不会走requestLayout()
```
            if (mLayoutParams.height != LayoutParams.WRAP_CONTENT
                    && mLayoutParams.height != LayoutParams.MATCH_PARENT) {
                autoSizeText();
                invalidate();
                return;
            }

            // Dynamic height, but height has stayed the same,
            // so use our new text layout.
            //新文本的高和原来文本的高度一致 这里不会走requestLayout()
            if (mLayout.getHeight() == oldht
                    && (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
                autoSizeText();
                invalidate();
                return;
            }
        }

        // We lose: the height has changed and we have a dynamic height.
        // Request a new view layout using our new text layout.
        requestLayout();
        invalidate();
    } else {
        // Dynamic width, so we have no choice but to request a new
        // view layout with a new text layout.
        nullLayouts();
        requestLayout();
        invalidate();
    }
}
这里我们可以看的出来,如果TextView的width属性不是wrap_content并且高度不会发生变化,那么就不会走requestLayout,TextView的高度不会发生变化有两种情况,一种是TextView的height被设置成一个精确值比如:android:layout_height="100dp" ,另外一种就是新文本的高度和原来的文本高度一致,这两种情况都只会调用invalidate方法。