TextView问题由来
TextView在中英文夹杂的时候,会出现自动断行的情况,相信许多人都碰见过。这是系统的一个Bug,在Android5.0以后被修复了,而5.0以下的还没有见到比较好的解决版本。
参考了网上的方法,有的朋友推荐使用全角和半角转换(没有解决问题),也有的推荐了JustifyTextView这个控件(效果也不理想)。
于是我决定自定义一个TextView来做这件事,勉强解决了问题,但是代价是失去了很多TextView自身拥有的特性,而且TextView自身做了很多缓存和优化的工作,Google强烈不建议我们去修改这个控件。
我们先来看看实现效果:
第1行:原生TextView 全是中文
第2行:原生TextView 全是英文
第3行:原生TextView,使用全角半角转换中英文夹杂
第4行:原生TextView 中英文夹杂
第5行:JustifyTextView 中英文夹杂
第6行:原生AdaptableTextView 中英文夹杂
怎么解决?
1、将text分割成多行
思路的第一步,就是将我们要设置的文本,例如”中英文夹杂testtesttesttesttesttesttesttesttesttest”这个String分割成多行保存在一个ArrayList()中。
我们可以首先获得TextView的宽度,然后根据这个宽度,把上面的String切割成多段。
由于“我”这个中文的在绘制时候,宽度比单个英文字母,例如是”m”大。所以我们一定要遍历每个字符,获得它的宽度。
/**
* 根据控件宽度,计算得出每行的字符串
*/
private void parseText(){
strs.clear();
int start = 0;//行起始Index
int curLineWidth = 0;//当前行宽
for (int i = 0; i < mText.length(); i++) {
char ch = mText.charAt(i);//获取当前字符
float[] widths = new float[1];
String srt = String.valueOf(ch);
mPaint.getTextWidths(srt, widths);//获取这个字符的宽度
if (ch == '\n'){//如果是换行符,则当独一行
strs.add(mText.substring(start, i));
start = i + 1;
curLineWidth = 0;
}else{
curLineWidth += (int) (Math.ceil(widths[0]));//计算当前宽度
if (curLineWidth > mLineWidth){//直到当前行宽度大于控件宽度,截取为一行
strs.add(mText.substring(start, i));
start = i;
i--;
curLineWidth = 0;
}else{
if (i == (mText.length() - 1)){//剩余的单独一行
strs.add(mText.substring(start, mText.length()));
}
}
}
}
}
2、使用drawText()方法,绘制出每一行
在上面的方法以后,我们就获得了每一行字符串了,剩下的工作就是用drawText()把每一行都绘制出来就可以了。另外还要注意一些,例如如果超出了maxLines,我们可以手动绘制一个省略号;还有padding的设置问题。
public void draw(Canvas canvas) {
int lines = mMaxLines > 0 && mMaxLines <= strs.size() ? mMaxLines : strs.size();
for (int i = 0; i < lines; i++) {
String text = strs.get(i);
//如果是最大行的最后一行但不是真实的最后一行则自动添加省略号
if(i == lines - 1 && i < strs.size() - 1)
text = text.substring(0, text.length() - 3) + "...";
canvas.drawText(text, getPaddingLeft(), getPaddingTop()+mPaint.getTextSize() + mLineHeight * i, mPaint);
}
}
3、重写TextView的onDraw()方法
简单来说,只要在ondraw()方法里面,调用我们自定义的draw()方法,绘制出文本即可。
@Override
@SuppressLint("NewApi")
protected void onDraw(Canvas canvas) {
getAdaptableText();
if(mIsDirty) {
mIsDirty = false;
String text = getText().toString();
int maxLines = getMaxLines();
if(!mAdaptableText.getText().equals(text))
mAdaptableText.setText(text);
if(mAdaptableText.getMaxLines() != maxLines)
mAdaptableText.setMaxLines(maxLines);
}
mAdaptableText.draw(canvas);
}
写在最后
TextView的这个Bug,几乎在所有的app都存在(微信什么的),也就可以看出,貌似没有很好的解决方法。
如果使用上面的自定义控件,看似解决了问题,其实带来TextView效率的严重下降,所有也强烈不建议在项目之中使用它(如果你们的app不追求效率又是另外一回事了)。