转载

TextView中英文排版混乱

TextView问题由来

TextView在 中英文夹杂的时候,会出现自动断行的情况 ,相信许多人都碰见过。这是系统的一个Bug,在Android5.0以后被修复了,而5.0以下的还没有见到比较好的解决版本。

参考了网上的方法,有的朋友推荐使用 全角和半角转换(没有解决问题) ,也有的推荐了 JustifyTextView这个控件(效果也不理想)

于是我决定 自定义一个TextView 来做这件事,勉强解决了问题,但是代价是失去了很多TextView自身拥有的特性,而且TextView自身做了很多缓存和优化的工作, Google强烈不建议我们去修改这个控件

我们先来看看实现效果:

TextView中英文排版混乱

第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不追求效率又是另外一回事了)。

下载源码

正文到此结束
Loading...