Android AutoWrapTextView 解决中英文排版

作者: 伪文艺大叔 | 来源:发表于2017-04-13 15:24 被阅读717次
    本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发

    最近项目有新需求,UED给了个卡券密码的UI样式,如图:

    xiaoguo.png

    我一看很简单啊,一个TextView解决问题,然后做好以后在模拟器里一看.....

    mnqxg.png

    纳尼,这个时候才想起来,TextView 中英文在一起会有排版问题,那怎么解决呢......

    思路

    刚开始的想法是一个字符一个字符的去绘制,绘制到最右边的临界点就换行绘制,结果实践以后发现不同的字符之间的间距不一样,显示会非常凌乱,又没有什么好的方案解决这个间距问题,所以这个方案pass;单个字符绘制不行那就一行一行绘制,根据View的长度把文本拆分成N行,然后一行一行的绘制。

    实现

    首先创建一个继承自View的AutoWrapTextView

    public class AutoWrapTextView extends View {
    
    }
    
    

    来看看它的构造方法

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

    init方法里分别调用了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方法就是初始化一个文本画笔

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

    接下来我们看看设置文本的方法setText方法

     public void setText(String text) {
          if (TextUtils.isEmpty(text)) return;
    
          //把文本转换成Char数组
          mTextCharArray = text.toCharArray();
          requestLayout();
     }
    

    首先把文本转换成Char数组,然后循环数组把整个文本拆分成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);
        }
    

    首先创建一个属性名为mSplitTextList的List集合用来存放拆分的文本;
    mSingleTextWidth 为单行文本显示的宽度;
    currentSingleTextWidth 为当前一行累计计算的宽度;

    然后开始循环Char数组,getSingleCharWidth方法就是计算单个Char的宽度;
    如果currentSingleTextWidth 小于 mSingleTextWidth 就把Char添加到lineStringBuffer 当中,如果是最后一个Char就直接把lineStringBuffer添加到mSplitTextList集合当中

    如果currentSingleTextWidth 大于 mSingleTextWidth,就把lineStringBuffer添加到mSplitTextList集合当中,重新给lineStringBuffer赋值,currentSingleTextWidth 归0;

    循环结束以后拆分好的文本就都添加到mSplitTextList集合当中了。

    拆分完成以后循环mSplitTextList集合,得到每一行文本的Rect值,绘制文本的时候会用到,然后设置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,然后循环文本绘制每一行文本内容。

    效果图

    我们来看下最后的效果

    atxgt.png
    结束语

    至此整个类的逻辑分析就结束了,想看完整源码的可以移步:
    https://github.com/chenpengfei88/AutoWrapTextView
    欢迎大家Star,Follow

    相关文章

      网友评论

      • Wing_Li:SpannableStringBuilder 可以解决问题吗?
        伪文艺大叔: @Wing_Li 没试过
      • fd1e786b155b:抱歉,楼主我没怎么看懂你说的中英文排版问题是指什么,加个换行符不是也可以像你下面效果图那样显示吗
        伪文艺大叔: @蛧虫 对
        fd1e786b155b:@伪文艺大叔 你的意思是说这段文字是接口返回的String字符串,然后要达到上面的效果吗
        伪文艺大叔:你不知道在哪里加换行符呀
      • 周南城:楼主你的AutoWrapTextView类没找到啊
        tiamosu:楼主实现的这个很不错,不够能添加支持SpannableStringBuilder就更好了
        周南城:@伪文艺大叔 噢噢,看到了:blush:
        伪文艺大叔: @urwater 主项目里有个library项目

      本文标题:Android AutoWrapTextView 解决中英文排版

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