Android AutoWrapTextView中英文排版问题的解决方法

时间:2021-05-21

前言

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


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


纳尼,这个时候才想起来,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,然后循环文本绘制每一行文本内容。

效果图

我们来看下最后的效果


结束语

至此整个类的逻辑分析就结束了,想看完整源码的可以移步:https://github.com/chenpengfei88/AutoWrapTextView或者通过本地进行下载:http://xiazai.jb51.net/201705/yuanma/AutoWrapTextView(jb51.net).rar

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章