天天看点

Android-TextView换行排版混乱-允许设置SpannableString

首先感谢两个网站提供的灵感:

http://www.it165.net/pro/html/201405/14606.html

另一个忘了地址了,哪天找出来加上

1.概述

TextView在换行时遇到首字符为字母、符号、汉字等时会产生提前换行、排版混乱等,看图

带ta看房处”,当第一个有字母开头时,提前换行

Android-TextView换行排版混乱-允许设置SpannableString

2.解决方案:1).半圆角转换 2).字符过滤成全英文字符

3).前两种解决都不够理想——自定义TextView才是王道

3.分析自定义步骤:

1).文字大小统一时,每当每行排满进行换行,然后绘制每行的内容

2).SpannableString设置了RelativeSizeSpan、ForegroundSpan等,甚至设置了多个,

需要逐字绘制,并在排满时进行换行

效果图:

Android-TextView换行排版混乱-允许设置SpannableString

前两个橙色背景是正常textview,黄色背景是自定义的效果

解决了允许以字母为首且不会提前换行的情况

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        paint = new Paint();
        /*
        设置宽度
         */
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);
                //父控件允许子控件的最大宽度,包括子控件设置的具体值或者fill_parent
        if(specMode == MeasureSpec.EXACTLY) { //fill_parent,也相当于设置了exactly
            mWidth = specSize;
        } else {
            //子控件需要的宽度
            int desiredSize = (int) (getPaddingLeft() + getPaddingRight() + mContent.length() * getTextSize());
            if(specMode == MeasureSpec.AT_MOST) { //wrap_content
                //两者取最小
                desiredSize = Math.min(specSize, desiredSize);
            }
            mWidth = desiredSize; //TextView的宽度
        }

//        /*
//        根据文本内容设置高度
//         */
//        Rect textBound = new Rect();
//        paint.setTextSize(getTextSize());
//        paint.getTextBounds(mContent,0,mContent.length(),textBound);
//        mHeight = textBound.height() + getPaddingBottom() + getPaddingTop();

        mContentWidth = mWidth - getPaddingLeft() - getPaddingRight(); //View里面内容的宽度
        setMeasuredDimension(mWidth, heightMeasureSpec);
    }

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
           

重写setText(Charsequence text, BufferType type) 方法,并获取SpannableString设置

@Override
    public void setText(CharSequence text, BufferType type) {
        //该行不能删掉,否则出错
        super.setText("", type);
        if(text instanceof SpannableString) {
            SpannableString sStr = (SpannableString) text;
            //获取所有的ForegroundSpan
            ForegroundColorSpan[] spans =  sStr.getSpans(, sStr.length(), ForegroundColorSpan.class);
            for(ForegroundColorSpan span:spans) {
                FGSpan fgSpan = new FGSpan();
                fgSpan.fgColor = span.getForegroundColor();
                fgSpan.start = sStr.getSpanStart(span);
                fgSpan.end = sStr.getSpanEnd(span);
                fgSpans.add(fgSpan);
            }
            //获取所有的RelativeSizeSpan
            RelativeSizeSpan[] spans2 = sStr.getSpans(, sStr.length(), RelativeSizeSpan.class);
            for(RelativeSizeSpan span:spans2) {
                RLSpan rlSpan = new RLSpan();
                rlSpan.propertion = span.getSizeChange();
                rlSpan.start = sStr.getSpanStart(span);
                rlSpan.end = sStr.getSpanEnd(span);
                rlSpans.add(rlSpan);
            }
        }
        //内容赋值
        mContent = text.toString(); //如果mContent不是static的话,xml里面text无法同步
    }
           

逐个字符绘制

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //逐个字符画出
        int fgSpanIndex = ; //标记fgSpans下标
        int rlSpanIndex = ; //标记rlSpans下标
        paint.setColor(getCurrentTextColor());
        paint.setTextSize(getTextSize());
        float x = ,y = ; //绘制字符时的x,y坐标
        Paint.FontMetrics fm = paint.getFontMetrics();
        float baseLine = fm.descent - fm.ascent;
        y = baseLine;
        for(int i = ; i < mContent.length(); i++) {
            /*
            画当前字符
             */
            //处理RelativeSizeSpan——若当前字符有RelativeSizeSpan设置,则进行设置
            if(rlSpans != null && rlSpans.size() > ) {
                if(rlSpanIndex < rlSpans.size()
                        && i >= rlSpans.get(rlSpanIndex).start
                        && i < rlSpans.get(rlSpanIndex).end) {
                    paint.setTextSize(rlSpans.get(rlSpanIndex).propertion*getTextSize());
                } else if(rlSpanIndex < rlSpans.size() && i >= rlSpans.get(rlSpanIndex).end) {
                    rlSpanIndex++;
                }
            }
            //处理ForegroundSpan——若当前字符有ForegroundSpan设置,则进行设置
            if(fgSpans != null && fgSpans.size() > ) {
                if(fgSpanIndex < fgSpans.size()
                        && i >= fgSpans.get(fgSpanIndex).start
                        && i < fgSpans.get(fgSpanIndex).end) {
                    paint.setColor(fgSpans.get(fgSpanIndex).fgColor);
                } else if(fgSpanIndex < fgSpans.size() && i >= fgSpans.get(fgSpanIndex).end) {
                    fgSpanIndex++;
                }
            }
            if(i + <=mContent.length()/*主要是防止后面i+1,i+2越界*/
                    && x >= mContentWidth - paint.measureText(mContent.substring(i+,i+))) { //换行
                x = ;
                y += baseLine + fm.leading;
            }
            canvas.drawText(mContent.substring(i,i+),x,y,paint);
            //计算下个字符水平位置
            x += paint.measureText(mContent.substring(i,i+));
            paint.setTextSize(getTextSize());
            paint.setColor(getCurrentTextColor());
        }
    }
           

主Activity里面进行设置

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyTextView myTextView = (MyTextView) findViewById(R.id.my);
        SpannableString sp1 = new SpannableString("是解决得快的健康减肥大力度dkfjkajdvb是解决得快的健康减肥大力度dkfjkajd是解决得快的健康减肥大力度dkfjkajd");
        sp1.setSpan(new ForegroundColorSpan(getResources()
                        .getColor(R.color.blue)), ,
                ,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        sp1.setSpan(new ForegroundColorSpan(getResources()
                        .getColor(R.color.red)), ,
                ,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        sp1.setSpan(new RelativeSizeSpan((float) ), ,
                ,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        sp1.setSpan(new ForegroundColorSpan(getResources().
                getColor(R.color.green_blue)),,
                ,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        sp1.setSpan(new RelativeSizeSpan((float) ), ,
                ,
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        myTextView.setText(sp1);
    }
           

此自定义的TextView不仅可以处理SpannableString属性,而且不用重新设置attr.xml文件,直接重写setText方法。

关于其它效果的设置,参考里面对SpannableString的处理,可以自行扩展,此处未进行高度设置,有简单方法的大牛等待共享

全部资源下载处:

http://download.csdn.net/detail/xiaoweiguoyuan/9404182