Android AutoWrapTextView 解決中英文排版問題

本文已授權(quán)微信公眾號(hào):鴻洋(hongyangAndroid)原創(chuàng)首發(fā)

最近項(xiàng)目有新需求,UED給了個(gè)卡券密碼的UI樣式,如圖:

xiaoguo.png

我一看很簡單啊,一個(gè)TextView解決問題,然后做好以后在模擬器里一看.....

mnqxg.png

納尼,這個(gè)時(shí)候才想起來,TextView 中英文在一起會(huì)有排版問題,那怎么解決呢......

思路

剛開始的想法是一個(gè)字符一個(gè)字符的去繪制,繪制到最右邊的臨界點(diǎn)就換行繪制,結(jié)果實(shí)踐以后發(fā)現(xiàn)不同的字符之間的間距不一樣,顯示會(huì)非常凌亂,又沒有什么好的方案解決這個(gè)間距問題,所以這個(gè)方案pass;單個(gè)字符繪制不行那就一行一行繪制,根據(jù)View的長度把文本拆分成N行,然后一行一行的繪制。

實(shí)現(xiàn)

首先創(chuàng)建一個(gè)繼承自View的AutoWrapTextView

public class AutoWrapTextView extends View {

}

來看看它的構(gòu)造方法

public AutoWrapTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
        initStyle(context, attrs);
        initPaint();
}

init方法里分別調(diào)用了initStyle方法和initPaint方法;
initStyle方法主要解析自定義的屬性

    private void initStyle(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AutoWrapTextViewStyle);
        mPaddingLeft = typedArray.getDimensionPixelSize(R.styleable.AutoWrapTextViewStyle_paddingLeft, 0);
        mPaddingRight = typedArray.getDimensionPixelSize(R.styleable.AutoWrapTextViewStyle_paddingRight, 0);
        mPaddingTop = typedArray.getDimensionPixelSize(R.styleable.AutoWrapTextViewStyle_paddingTop, 0);
        mPaddingBottom = typedArray.getDimensionPixelSize(R.styleable.AutoWrapTextViewStyle_paddingBottom, 0);

        mTextColor = typedArray.getColor(R.styleable.AutoWrapTextViewStyle_textColor, Color.BLACK);
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.AutoWrapTextViewStyle_textSize, 50);
        mLineSpacingExtra = typedArray.getInteger(R.styleable.AutoWrapTextViewStyle_lineSpacingExtra, 7);

        typedArray.recycle();
    }

屬性名含義都很明顯不用過多解釋,initPaint方法就是初始化一個(gè)文本畫筆

 private void initPaint() {
        mTextPaint = new TextPaint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextAlign(Paint.Align.LEFT);
 }

接下來我們看看設(shè)置文本的方法setText方法

 public void setText(String text) {
      if (TextUtils.isEmpty(text)) return;

      //把文本轉(zhuǎn)換成Char數(shù)組
      mTextCharArray = text.toCharArray();
      requestLayout();
 }

首先把文本轉(zhuǎn)換成Char數(shù)組,然后循環(huán)數(shù)組把整個(gè)文本拆分成N行文本,下面來看看核心方法splitText方法

    private void splitText(int heightMode) {
        if (mTextCharArray == null) return;

        mSplitTextList = new ArrayList<>();
        mSingleTextWidth = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
        int currentSingleTextWidth = 0;
        StringBuffer lineStringBuffer = new StringBuffer();
        for (int i = 0, length = mTextCharArray.length; i < length; i++) {
            char textChar = mTextCharArray[i];
            currentSingleTextWidth += getSingleCharWidth(textChar);
            if (currentSingleTextWidth > mSingleTextWidth) {
                mSplitTextList.add(lineStringBuffer.toString());
                lineStringBuffer = new StringBuffer();
                currentSingleTextWidth = 0;
                i--;
            } else {
                lineStringBuffer.append(textChar);
                if (i == length - 1) mSplitTextList.add(lineStringBuffer.toString());
            }
        }

        int textHeight = 0;
        mSplitTextRectArray = new Rect[mSplitTextList.size()];
        for (int m = 0, length = mSplitTextList.size(); m < length; m++) {
            String lineText = mSplitTextList.get(m);
            Rect lineTextRect = new Rect();
            mTextPaint.getTextBounds(lineText, 0, lineText.length(), lineTextRect);
            if (heightMode == MeasureSpec.AT_MOST) {
                textHeight += (lineTextRect.height() + mLineSpacingExtra);
                if (m == length - 1) {
                    textHeight = textHeight + mPaddingBottom + mPaddingTop;
                }
            } else {
                if (textHeight == 0)
                    textHeight = getMeasuredHeight();
            }
            mSplitTextRectArray[m] = lineTextRect;
        }

        setMeasuredDimension(getMeasuredWidth(), textHeight);
    }

首先創(chuàng)建一個(gè)屬性名為mSplitTextList的List集合用來存放拆分的文本;
mSingleTextWidth 為單行文本顯示的寬度;
currentSingleTextWidth 為當(dāng)前一行累計(jì)計(jì)算的寬度;

然后開始循環(huán)Char數(shù)組,getSingleCharWidth方法就是計(jì)算單個(gè)Char的寬度;
如果currentSingleTextWidth 小于 mSingleTextWidth 就把Char添加到lineStringBuffer 當(dāng)中,如果是最后一個(gè)Char就直接把lineStringBuffer添加到mSplitTextList集合當(dāng)中

如果currentSingleTextWidth 大于 mSingleTextWidth,就把lineStringBuffer添加到mSplitTextList集合當(dāng)中,重新給lineStringBuffer賦值,currentSingleTextWidth 歸0;

循環(huán)結(jié)束以后拆分好的文本就都添加到mSplitTextList集合當(dāng)中了。

拆分完成以后循環(huán)mSplitTextList集合,得到每一行文本的Rect值,繪制文本的時(shí)候會(huì)用到,然后設(shè)置View的寬高。

接下來就是繪制方法drawText

 public void drawText(Canvas canvas) {
        if (mSplitTextList == null || mSplitTextList.size() == 0) return;

        int marginTop = getTopTextMarginTop();
        for (int m = 0, length = mSplitTextList.size(); m < length; m++) {
            String lineText = mSplitTextList.get(m);
            canvas.drawText(lineText, mPaddingLeft, marginTop, mTextPaint);
            marginTop += (mSplitTextRectArray[m].height() + mLineSpacingExtra);
        }
 }

首先得到第一行文本距離頂部的高度marginTop,然后循環(huán)文本繪制每一行文本內(nèi)容。

效果圖

我們來看下最后的效果

atxgt.png
結(jié)束語

至此整個(gè)類的邏輯分析就結(jié)束了,想看完整源碼的可以移步:
https://github.com/chenpengfei88/AutoWrapTextView
歡迎大家Star,F(xiàn)ollow

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容