TextView中英文排版混乱

TextView问题由来

TextView在中英文夹杂的时候,会出现自动断行的情况,相信许多人都碰见过。这是系统的一个Bug,在Android5.0以后被修复了,而5.0以下的还没有见到比较好的解决版本。
参考了网上的方法,有的朋友推荐使用全角和半角转换(没有解决问题),也有的推荐了JustifyTextView这个控件(效果也不理想)
于是我决定自定义一个TextView来做这件事,勉强解决了问题,但是代价是失去了很多TextView自身拥有的特性,而且TextView自身做了很多缓存和优化的工作,Google强烈不建议我们去修改这个控件
我们先来看看实现效果:
TextView中英文排版混乱_第1张图片

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

下载源码

你可能感兴趣的:(android开发)