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