美文网首页自定义控件
解决TextView排版问题

解决TextView排版问题

作者: 方_f666 | 来源:发表于2020-04-03 18:02 被阅读0次

方法一(Util包):
/**
* 解决文本中有数字英文字母时的排版不齐问题
* @param tv 被排版的TextView
* @return
*/
public static String autoSplitText(final TextView tv){
final String rawText=tv.getText().toString(); //原始文本
final Paint tvPaint=tv.getPaint(); //paint,包含字体等信息
final float tvWidth=tv.getWidth()-tv.getPaddingLeft()-tv.getPaddingRight(); //控件可用宽度

    //将原始文本按行拆分
    String[]rawTextLines=rawText.replaceAll("\r","").split("\n");
    StringBuilder sbNewText=new StringBuilder();
    for(String rawTextLine:rawTextLines){
        if(tvPaint.measureText(rawTextLine)<=tvWidth){
            //如果整行宽度在控件可用宽度之内,就不处理了
            sbNewText.append(rawTextLine);
        }else{
            //如果整行宽度超过控件可用宽度,则按字符测量,在超过可用宽度的前一个字符处手动换行
            float lineWidth=0;
            int count = 0;
            for(int cnt=0;cnt!=rawTextLine.length();++cnt){
                char ch=rawTextLine.charAt(cnt);
                lineWidth+=tvPaint.measureText(String.valueOf(ch));
                if(lineWidth<=tvWidth){
                    sbNewText.append(ch);
                }else {
                    sbNewText.append("\n");
                    lineWidth = 0;
                    --cnt;
                }
            }
        }
        sbNewText.append("\n");
    }

    //把结尾多余的\n去掉
    if(!rawText.endsWith("\n")){
        sbNewText.deleteCharAt(sbNewText.length()-1);
    }

    return sbNewText.toString();
}

方法二(自定义View):
/**

  • Desc:文字排版整齐的TextView
    /
    public class AutoTextView extends View {
    /
    *

    • 水平居中
      /
      private static final int CENTER_HORIZONTAL = 1;
      /
      *
    • 垂直居中
      /
      private static final int CENTER_VERTICAL = 2;
      /
      *
    • 居中(水平且垂直)
      */
      private static final int CENTER = 3;

    private static final String TAG = JeejioTextView.class.getSimpleName();

    /**

    • 字体大小
      /
      private int mTextSize;
      /
      *
    • 字体颜色
      /
      private int mTextColor;
      /
      *
    • 文本内容
      /
      private String mTextContent;
      /
      *
    • 行间距
      /
      private int mLineSpace;
      /
      *
    • 最大行数
      /
      private int mMaxLines;
      /
      *
    • 画笔
      /
      private Paint mPaint;
      private Paint.FontMetricsInt mTextFM;
      /
      *
    • 行高
      /
      private int mSignleLineHeight;
      /
      *
    • 屏幕高度
      */
      private int mScreenHeght;

    private int mScreenWidth;
    /**

    • 当前内容宽度
      /
      private int mViewWidth;
      /
      *
    • 当前内容高度
      /
      private int mViewHeight;
      /
      *
    • 视图最小高度
      /
      private int mMinHeight;
      /
      *
    • 视图最小宽度
      /
      private int mMinWidth;
      /
      *
    • 视图最大宽度
      /
      private int mMaxWidth;
      /
      *
    • 视图最大高度
      /
      private int mMaxHeight;
      /
      *
    • 内容布局居中
      */
      private int mGravity;

    /**

    • 文本行数
      */
      private int mLineCount;
public JeejioTextView(Context context) {
    this(context, null);
}

public JeejioTextView(Context context, AttributeSet attrs) {
    this(context, attrs, -1);
}

public JeejioTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
    initPaint();
}

private void initPaint() {
    if (mPaint == null) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }
    mPaint.setTextSize(mTextSize);
    mPaint.setStrokeWidth(1);
    mPaint.setColor(mTextColor);
    mPaint.setTextAlign(Paint.Align.LEFT);
    mTextFM = mPaint.getFontMetricsInt();
    mSignleLineHeight = Math.abs(mTextFM.top - mTextFM.bottom);
}


@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    mScreenWidth = displayMetrics.widthPixels;
    mScreenHeght = displayMetrics.heightPixels;
}

private void init(Context context, AttributeSet attrs) {
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.JeejioTextView);
    mTextSize = typedArray.getDimensionPixelSize(R.styleable.JeejioTextView_textSize, (int) sp2px(context, 16));
    mMaxWidth = typedArray.getDimensionPixelSize(R.styleable.JeejioTextView_maxWidth, 0);
    mGravity = typedArray.getInteger(R.styleable.JeejioTextView_gravity, 0);
    mMaxHeight = typedArray.getDimensionPixelSize(R.styleable.JeejioTextView_maxHeight, 0);
    mMinHeight = typedArray.getDimensionPixelSize(R.styleable.JeejioTextView_minHeight, 0);
    mMinWidth = typedArray.getDimensionPixelSize(R.styleable.JeejioTextView_minWidth, 0);
    mTextColor = typedArray.getColor(R.styleable.JeejioTextView_myTextColor, Color.BLACK);
    mTextContent = typedArray.getString(R.styleable.JeejioTextView_myText);
    mLineSpace = typedArray.getDimensionPixelSize(R.styleable.JeejioTextView_lineSpace, 0);
    mMaxLines = typedArray.getInteger(R.styleable.JeejioTextView_maxLine, 0);
    typedArray.recycle();
}

/**
 * sp -> px 将sp转换为px
 *
 * @param context
 * @param sp
 * @return
 */
private float sp2px(Context context, int sp) {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics());
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    mViewWidth = getViewWidth(widthMeasureSpec);
    mViewHeight = getViewHeight(heightMeasureSpec);
    setMeasuredDimension(mViewWidth, mViewHeight);
}

/**
 * Date: 2020/1/4  15:18
 * Desc:通过父容器以及自身高度  确定最终高度
 */
private int getViewHeight(int heightMeasureSpec) {
    if (TextUtils.isEmpty(mTextContent)) {
        return 0;
    }

    int height = 0;
    int size = MeasureSpec.getSize(heightMeasureSpec);
    int mode = MeasureSpec.getMode(heightMeasureSpec);
    switch (mode) {
        case MeasureSpec.EXACTLY:
            height = size;
            getRealHeight();
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.UNSPECIFIED:
            if (!TextUtils.isEmpty(mTextContent)) {
                height = (int) getRealHeight();
            }
            break;
    }
    // 如果存在最小高度
    if (mMinHeight != 0 && height < mMinHeight) {
        height = mMinHeight;
    }

    // 如果存在最大高度
    if (mMaxHeight != 0 && height > mMaxHeight) {
        height = mMaxHeight;
    }
    return height;
}


/**
 * Date: 2020/1/4  15:17
 * Desc:获取视图高度
 */
private float getRealHeight() {
    String[] split = mTextContent.split("\n");
    int count = 0;
    for (int i = 0; i < split.length; i++) {
        String text = split[i];
        float textWidth = mPaint.measureText(text, 0, text.length());
        float temCount = textWidth * 1.0f / (getMaxContentWidth());
        if (temCount >= 1) {
            count = (int) (count + Math.ceil(temCount));
        } else {
            count++;
        }
    }
    if (mMaxLines != 0 && count > mMaxLines) {
        count = mMaxLines;
    }
    mLineCount = count;
    return count * mSignleLineHeight + mLineSpace * (count - 1) + getPaddingBottom() + getPaddingTop();
}

/**
 * Date: 2020/1/4  15:16
 * Desc:通过父容器以及自身宽度  确定最终宽度
 */
private int getViewWidth(int widthMeasureSpec) {
    if (TextUtils.isEmpty(mTextContent)) {
        return 0;
    }
    int width = 0;
    int size = MeasureSpec.getSize(widthMeasureSpec);
    int mode = MeasureSpec.getMode(widthMeasureSpec);
    switch (mode) {
        case MeasureSpec.EXACTLY:
            width = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.UNSPECIFIED:
            if (!TextUtils.isEmpty(mTextContent)) {
                width = getRealWidth() + getPaddingLeft() + getPaddingRight();
            }
            break;
    }
    // 如果存在最小宽度
    if (mMinWidth != 0 && width < mMinWidth) {
        width = mMinWidth;
    }

    // 如果存在最大宽度
    if (mMaxWidth != 0 && width > mMaxWidth) {
        width = mMaxWidth;
    }
    return width;
}

/**
 * Date: 2020/1/4  15:16
 * Desc:获取视图高度
 */
private int getRealWidth() {
    int width = 0;
    String[] split = mTextContent.split("\n");
    for (int i = 0; i < split.length; i++) {
        String text = split[i];
        float textWidth = mPaint.measureText(text, 0, text.length());
        width = (int) Math.max(width, textWidth);
    }
    if (width > mScreenWidth - getPaddingRight() - getPaddingLeft()) {
        width = mScreenWidth - getPaddingRight() - getPaddingLeft();
    }
    return width;
}

/**
 * Date: 2020/3/16  14:51
 * Desc:获取内容最大宽度
 */
private int getMaxContentWidth() {
    int maxWidth = 0;
    float contentWidth = mViewWidth - getPaddingRight() - getPaddingLeft();
    String[] split = mTextContent.split("\n");
    for (int i = 0; i < split.length; i++) {
        String text = split[i];
        if (TextUtils.isEmpty(text)) {
            continue;
        }
        float textWidth = mPaint.measureText(text, 0, text.length());
        int line = (int) Math.ceil(textWidth * 1.0f / (contentWidth));
        for (int j = 0; j < line; j++) {
            String subText = getTextByIndex(text, (int) contentWidth);
            float subtextWidth = mPaint.measureText(subText, 0, subText.length());
            maxWidth = (int) Math.max(subtextWidth, maxWidth);
            text = text.substring(subText.length());
        }
    }
    return maxWidth;
}


@Override
protected void onDraw(Canvas canvas) {
    if (mTextContent != null && mTextContent.length() > 0) {
        initPaint();
        float startL = 0.0f;
        float startT = 0.0f;
        float contentHeight = getRealHeight();
        float contentWidth = mViewWidth - getPaddingRight() - getPaddingLeft();
        int maxContentWidth = getMaxContentWidth();
        startL += getPaddingLeft();
        int textTop = mSignleLineHeight / 2 + (mTextFM.bottom - mTextFM.top) / 2 - mTextFM.bottom;
        startT += getPaddingTop() + textTop;
        String[] split = mTextContent.split("\n");
        for (int i = 0; i < split.length; i++) {
            String text = split[i];
            if (TextUtils.isEmpty(text)) {
                startT += mSignleLineHeight + mLineSpace;
                continue;
            }
            float textWidth = mPaint.measureText(text, 0, text.length());
            int line = (int) Math.ceil(textWidth * 1.0f / maxContentWidth);
            for (int j = 0; j < line; j++) {
                String subText = getTextByIndex(text, (int) contentWidth);
                float subtextWidth = mPaint.measureText(subText, 0, subText.length());
                if (i == 0 && j == 0) {
                    if (split.length + line - 1 > 1) {
                        switch (mGravity) {
                            // 水平居中
                            case CENTER_HORIZONTAL:
                                if (contentWidth - subtextWidth > 0) {
                                    startL = mViewWidth / 2 - maxContentWidth / 2;
                                }
                                break;
                            // 垂直居中
                            case CENTER_VERTICAL:
                                if (contentHeight > 0 && contentHeight < mViewHeight) {
                                    startT = (mViewHeight / 2 - contentHeight / 2) + textTop;
                                }
                                break;
                            // 居中
                            case CENTER:
                                if (contentHeight > 0 && contentHeight < mViewHeight) {
                                    startT = (mViewHeight / 2 - contentHeight / 2) + textTop;
                                }

                                if (contentWidth - subtextWidth > 0) {
                                    startL = mViewWidth / 2 - maxContentWidth / 2;
                                }
                                break;
                        }
                    } else {
                        switch (mGravity) {
                            // 水平居中
                            case CENTER_HORIZONTAL:
                                startL = mViewWidth / 2 - subtextWidth / 2;
                                break;
                            // 垂直居中
                            case CENTER_VERTICAL:
                                startT = mViewHeight / 2 - mSignleLineHeight / 2 + textTop;
                                break;
                            // 居中
                            case CENTER:
                                startL = mViewWidth / 2 - subtextWidth / 2;
                                startT = mViewHeight / 2 - mSignleLineHeight / 2 + textTop;
                                break;
                        }
                    }
                }
                canvas.drawText(subText, startL, startT, mPaint);
                text = text.substring(subText.length());
                startT += mSignleLineHeight + mLineSpace;
            }
        }
    }
}

private String getTextByIndex(String text, int mMaxWidth) {
    int start = 0;
    int end = text.length();
    int middle;
    String curText = "";
    boolean error = false;
    while (start <= end) {
        middle = (start + end) / 2;
        String substring = text.substring(0, middle);
        curText = substring;
        float substringWidth = mPaint.measureText(substring, 0, substring.length());
        if (substringWidth > mMaxWidth) {
            end = middle - 1;
            error = true;
        } else if (substringWidth < mMaxWidth) {
            start = middle + 1;
            error = false;
        } else {
            error = false;
            break;
        }
    }
    if (error) {
        curText = curText.substring(0, curText.length() - 1);
    }
    return curText;
}


/**
 * Date: 2020/1/4  15:17
 * Desc:设置字体大小
 */
public void setTextSize(int textSize) {
    setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}

/**
 * Date: 2020/2/17  17:07
 * Desc:设置字体大小
 *
 * @param unit 转换类型
 * @param size 字体大小
 */
public void setTextSize(int unit, int size) {
    this.mTextSize = (int) TypedValue.applyDimension(unit, size, getResources().getDisplayMetrics());
    initPaint();
    requestLayout();
    invalidate();
}

/**
 * Date: 2020/1/4  15:17
 * Desc:设置字体颜色
 */
public void setTextColor(int textColor) {
    this.mTextColor = textColor;
    invalidate();
}


/**
 * Date: 2020/1/4  15:17
 * Desc:设置文本
 */
public void setText(String textContent) {
    this.mTextContent = textContent;
    requestLayout();
    invalidate();
}


/**
 * Date: 2020/1/6  13:53
 * Desc:获取当前文本
 */
public CharSequence getText() {
    return mTextContent;
}

/**
 * Date: 2020/2/17  17:11
 * Desc:获取文本行数
 */
public int getLineCount() {
    return mLineCount;
}

/**
 * Date: 2020/2/18  13:43
 * Desc:获取字体大小
 */
public float getTextSize() {
    return mTextSize;
}

}

相关文章

网友评论

    本文标题:解决TextView排版问题

    本文链接:https://www.haomeiwen.com/subject/bbykphtx.html