Android 自绘TextView解决提前换行问题,支持图文混排

本文转载自:Android 自绘TextView解决提前换行问题,支持图文混排


先看下效果图:Android 自绘TextView解决提前换行问题,支持图文混排_第1张图片

上面是MTextView,下面是默认的TextView。

 

一、原因

用最简单的全英文句子为例,如果有一个很长的单词,这一行剩余的空间显示不下了,那么规则就是不打断单词,而是把整个单词丢到下一行开始显示。这样本来没有错。一是咱们中国人都是方块字,怎么都放得下,不存在英文的这个问题。所以不习惯那个排版。二是如果TextView里面有图片,如图,不知道判断单词的代码是怎么弄得,总之它觉得最后一个啦字和后面的一串表情应该是一个整体,不能分开,就一起丢到第二行了,也就造成了这种难看的排版。要验证这个说法也很简单,自己去QQ里试一试,在每个表情之间都加一个空格,就会发现排版一下子正常了。

二、解决方法

    最简单的就是表情之间加空格,如果不想这么做,就只有自己来画啦。

    先给初学的朋友解释一下View绘制的流程,首先是onMeasure(int widthMeasureSpec, int heightMeasureSpec),onMeasure执行的时候,就是父View在问你,小朋友,你要占多大的地儿呀?当然,问你的时候,会给你个限制条件,就是那两参数,以widthMeasureSpec为例,这参数不能直接用,得先拆开,用int widthMode = MeasureSpec.getMode(widthMeasureSpec) 和 int widthSize = MeasureSpec.getSize(widthMeasureSpec);widthMode就三种情况:

MeasureSpec.EXACTLY:你就widthSize那么宽就行了。

MeasureSpec.AT_MOST:你最多只能widthSize那么宽。

MeasureSpec.UNSPECIFIED:未指定,你爱多宽多宽。

当然,其实这只父View给你的建议,遵不遵守你自己看着办,但是自己乱来导致显示不全就不是父View的错了。

最终你听取了建议,思量了一番,觉得自己应该有width那么宽,height那么高,最后就得用setMeasuredDimension(width, height)这个函数真正确定自己的高宽。然后onMeasure()的工作就完了。

然后就是onDraw(Canvas canvas),这个就简单了,canvas就是父View给的一块画布,爱在上面画啥都行,比如写个字drawText(String text,float x, float y, Paint paint),

text是要写的字,paint是写字的笔,值得注意的是x,y坐标是相对于你自己这一小块画布的左上角的。最左上就是0,0右下是width,height

 

上代码

[java]  view plain copy
  1. </pre><pre>  

[java]  view plain copy
  1. /** 
  2.  * @author huangwei 
  3.  * @version SocialClient 1.2.0 
  4.  * @功能 图文混排TextView,请使用{@link #setMText(CharSequence)} 
  5.  * @2014年5月27日 
  6.  * @下午5:29:27 
  7.  */  
  8. public class MTextView extends TextView  
  9. {  
  10.     /** 
  11.      * 缓存测量过的数据 
  12.      */  
  13.     private static HashMap<String, SoftReference<MeasuredData>> measuredData = new HashMap<String, SoftReference<MeasuredData>>();  
  14.     private static int hashIndex = 0;  
  15.     /** 
  16.      * 存储当前文本内容,每个item为一行 
  17.      */  
  18.     ArrayList<LINE> contentList = new ArrayList<LINE>();  
  19.     private Context context;  
  20.     /** 
  21.      * 用于测量字符宽度 
  22.      */  
  23.     private TextPaint paint = new TextPaint();  
  24.       
  25. //  private float lineSpacingMult = 0.5f;  
  26.     private int textColor = Color.BLACK;  
  27.     //行距  
  28.     private float lineSpacing;  
  29.     private int lineSpacingDP = 5;  
  30.     /** 
  31.      * 最大宽度 
  32.      */  
  33.     private int maxWidth;  
  34.     /** 
  35.      * 只有一行时的宽度 
  36.      */  
  37.     private int oneLineWidth = -1;  
  38.     /** 
  39.      * 已绘的行中最宽的一行的宽度 
  40.      */  
  41.     private float lineWidthMax = -1;  
  42.     /** 
  43.      * 存储当前文本内容,每个item为一个字符或者一个SpanObject 
  44.      */  
  45.     private ArrayList<Object> obList = new ArrayList<Object>();  
  46.     /** 
  47.      * 是否使用默认{@link #onMeasure(int, int)}和{@link #onDraw(Canvas)} 
  48.      */  
  49.     private boolean useDefault = false;  
  50.     private CharSequence text = "";  
  51.   
  52.     private int minHeight;  
  53.     /** 
  54.      * 用以获取屏幕高宽 
  55.      */  
  56.     private DisplayMetrics displayMetrics;  
  57.     /** 
  58.      * {@link android.text.style.BackgroundColorSpan}用 
  59.      */  
  60.     private Paint textBgColorPaint = new Paint();  
  61.     /** 
  62.      * {@link android.text.style.BackgroundColorSpan}用 
  63.      */  
  64.     private Rect textBgColorRect = new Rect();  
  65.   
  66.     public MTextView(Context context)  
  67.     {  
  68.         super(context);  
  69.         this.context = context;  
  70.         paint.setAntiAlias(true);  
  71.         lineSpacing = dip2px(context, lineSpacingDP);  
  72.         minHeight = dip2px(context, 30);  
  73.   
  74.         displayMetrics = new DisplayMetrics();  
  75.     }  
  76.       
  77.     public MTextView(Context context,AttributeSet attrs)  
  78.     {  
  79.         super(context,attrs);  
  80.         this.context = context;  
  81.         paint.setAntiAlias(true);  
  82.         lineSpacing = dip2px(context, lineSpacingDP);  
  83.         minHeight = dip2px(context, 30);  
  84.   
  85.         displayMetrics = new DisplayMetrics();  
  86.     }  
  87.   
  88.     public static int px2sp(Context context, float pxValue)  
  89.     {  
  90.         final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;  
  91.         return (int) (pxValue / fontScale + 0.5f);  
  92.     }  
  93.   
  94.     /** 
  95.      * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 
  96.      */  
  97.     public static int dip2px(Context context, float dpValue)  
  98.     {  
  99.         final float scale = context.getResources().getDisplayMetrics().density;  
  100.         return (int) (dpValue * scale + 0.5f);  
  101.     }  
  102.   
  103.     @Override  
  104.     public void setMaxWidth(int maxpixels)  
  105.     {  
  106.         super.setMaxWidth(maxpixels);  
  107.         maxWidth = maxpixels;  
  108.     }  
  109.   
  110.     @Override  
  111.     public void setMinHeight(int minHeight)  
  112.     {  
  113.         super.setMinHeight(minHeight);  
  114.         this.minHeight = minHeight;  
  115.     }  
  116.   
  117.     @Override  
  118.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
  119.     {  
  120.         if (useDefault)  
  121.         {  
  122.             super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  123.             return;  
  124.         }  
  125.         int width = 0, height = 0;  
  126.   
  127.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  128.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  129.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  130.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  131.   
  132.         switch (widthMode)  
  133.         {  
  134.         case MeasureSpec.EXACTLY:  
  135.             width = widthSize;  
  136.             break;  
  137.         case MeasureSpec.AT_MOST:  
  138.             width = widthSize;  
  139.             break;  
  140.         case MeasureSpec.UNSPECIFIED:  
  141.   
  142.             ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);  
  143.             width = displayMetrics.widthPixels;  
  144.             break;  
  145.         default:  
  146.             break;  
  147.         }  
  148.         if (maxWidth > 0)  
  149.             width = Math.min(width, maxWidth);  
  150.   
  151.         paint.setTextSize(this.getTextSize());  
  152.         paint.setColor(textColor);  
  153.         int realHeight = measureContentHeight((int) width);  
  154.   
  155.         //如果实际行宽少于预定的宽度,减少行宽以使其内容横向居中  
  156.         int leftPadding = getCompoundPaddingLeft();  
  157.         int rightPadding = getCompoundPaddingRight();  
  158.         width = Math.min(width, (int) lineWidthMax + leftPadding + rightPadding);  
  159.   
  160.         if (oneLineWidth > -1)  
  161.         {  
  162.             width = oneLineWidth;  
  163.         }  
  164.         switch (heightMode)  
  165.         {  
  166.         case MeasureSpec.EXACTLY:  
  167.             height = heightSize;  
  168.             break;  
  169.         case MeasureSpec.AT_MOST:  
  170.             height = realHeight;  
  171.             break;  
  172.         case MeasureSpec.UNSPECIFIED:  
  173.             height = realHeight;  
  174.             break;  
  175.         default:  
  176.             break;  
  177.         }  
  178.   
  179.         height += getCompoundPaddingTop() + getCompoundPaddingBottom();  
  180.   
  181.         height = Math.max(height, minHeight);  
  182.   
  183.         setMeasuredDimension(width, height);  
  184.     }  
  185.   
  186.     @Override  
  187.     protected void onDraw(Canvas canvas)  
  188.     {  
  189.         if (useDefault)  
  190.         {  
  191.             super.onDraw(canvas);  
  192.             return;  
  193.         }  
  194.         if (contentList.isEmpty())  
  195.             return;  
  196.         int width;  
  197.   
  198.         Object ob;  
  199.   
  200.         int leftPadding = getCompoundPaddingLeft();  
  201.         int topPadding = getCompoundPaddingTop();  
  202.   
  203.         float height = 0 + topPadding + lineSpacing;  
  204.         //只有一行时  
  205.         if (oneLineWidth != -1)  
  206.         {  
  207.             height = getMeasuredHeight() / 2 - contentList.get(0).height / 2;  
  208.         }  
  209.   
  210.         for (LINE aContentList : contentList)  
  211.         {  
  212.             //绘制一行  
  213.             float realDrawedWidth = leftPadding;  
  214.             for (int j = 0; j < aContentList.line.size(); j++)  
  215.             {  
  216.                 ob = aContentList.line.get(j);  
  217.                 width = aContentList.widthList.get(j);  
  218.   
  219.                 if (ob instanceof String)  
  220.                 {  
  221.                     canvas.drawText((String) ob, realDrawedWidth, height + aContentList.height - paint.getFontMetrics().descent, paint);  
  222.                     realDrawedWidth += width;  
  223.                 }  
  224.                 else if (ob instanceof SpanObject)  
  225.                 {  
  226.                     Object span = ((SpanObject) ob).span;  
  227.                     if(span instanceof ImageSpan)  
  228.                     {  
  229.                         ImageSpan is = (ImageSpan) span;  
  230.                         Drawable d = is.getDrawable();  
  231.       
  232.                         int left = (int) (realDrawedWidth);  
  233.                         int top = (int) height;  
  234.                         int right = (int) (realDrawedWidth + width);  
  235.                         int bottom = (int) (height + aContentList.height);  
  236.                         d.setBounds(left, top, right, bottom);  
  237.                         d.draw(canvas);  
  238.                         realDrawedWidth += width;  
  239.                     }  
  240.                     else if(span instanceof BackgroundColorSpan)  
  241.                     {  
  242.                           
  243.                         textBgColorPaint.setColor(((BackgroundColorSpan) span).getBackgroundColor());  
  244.                         textBgColorPaint.setStyle(Style.FILL);  
  245.                         textBgColorRect.left = (int) realDrawedWidth;  
  246.                         int textHeight = (int) getTextSize();  
  247.                         textBgColorRect.top = (int) (height + aContentList.height - textHeight - paint.getFontMetrics().descent);  
  248.                         textBgColorRect.right = textBgColorRect.left+width;  
  249.                         textBgColorRect.bottom = (int) (height + aContentList.height + lineSpacing  - paint.getFontMetrics().descent);  
  250.                         canvas.drawRect(textBgColorRect, textBgColorPaint);  
  251.                         canvas.drawText(((SpanObject) ob).source.toString(), realDrawedWidth, height + aContentList.height - paint.getFontMetrics().descent, paint);  
  252.                         realDrawedWidth += width;  
  253.                     }  
  254.                     else//做字符串处理  
  255.                     {  
  256.                         canvas.drawText(((SpanObject) ob).source.toString(), realDrawedWidth, height + aContentList.height - paint.getFontMetrics().descent, paint);  
  257.                         realDrawedWidth += width;  
  258.                     }  
  259.                 }  
  260.   
  261.             }  
  262.             height += aContentList.height + lineSpacing;  
  263.         }  
  264.   
  265.     }  
  266.   
  267.     @Override  
  268.     public void setTextColor(int color)  
  269.     {  
  270.         super.setTextColor(color);  
  271.         textColor = color;  
  272.     }  
  273.   
  274.     /** 
  275.      * 用于带ImageSpan的文本内容所占高度测量 
  276.      * @param width 预定的宽度 
  277.      * @return 所需的高度 
  278.      */  
  279.     private int measureContentHeight(int width)  
  280.     {  
  281.         int cachedHeight = getCachedData(text.toString(), width);  
  282.   
  283.         if (cachedHeight > 0)  
  284.         {  
  285.             return cachedHeight;  
  286.         }  
  287.   
  288.         // 已绘的宽度  
  289.         float obWidth = 0;  
  290.         float obHeight = 0;  
  291.   
  292.         float textSize = this.getTextSize();  
  293.         FontMetrics fontMetrics = paint.getFontMetrics();  
  294.         //行高  
  295.         float lineHeight = fontMetrics.bottom - fontMetrics.top;  
  296.         //计算出的所需高度  
  297.         float height = lineSpacing;  
  298.   
  299.         int leftPadding = getCompoundPaddingLeft();  
  300.         int rightPadding = getCompoundPaddingRight();  
  301.   
  302.         float drawedWidth = 0;  
  303.           
  304.         boolean splitFlag = false;//BackgroundColorSpan拆分用  
  305.   
  306.         width = width - leftPadding - rightPadding;  
  307.   
  308.         oneLineWidth = -1;  
  309.   
  310.         contentList.clear();  
  311.   
  312.         StringBuilder sb;  
  313.   
  314.         LINE line = new LINE();  
  315.   
  316.         for (int i = 0; i < obList.size(); i++)  
  317.         {  
  318.             Object ob = obList.get(i);  
  319.   
  320.             if (ob instanceof String)  
  321.             {  
  322.   
  323.                 obWidth = paint.measureText((String) ob);  
  324.                 obHeight = textSize;  
  325.             }  
  326.             else if (ob instanceof SpanObject)  
  327.             {  
  328.                 Object span = ((SpanObject) ob).span;  
  329.                 if(span instanceof ImageSpan)  
  330.                 {  
  331.                     Rect r = ((ImageSpan)span).getDrawable().getBounds();  
  332.                     obWidth = r.right - r.left;  
  333.                     obHeight = r.bottom - r.top;  
  334.                     if (obHeight > lineHeight)  
  335.                         lineHeight = obHeight;  
  336.                 }  
  337.                 else if(span instanceof BackgroundColorSpan)  
  338.                 {  
  339.                     String str = ((SpanObject) ob).source.toString();  
  340.                     obWidth = paint.measureText(str);  
  341.                     obHeight = textSize;  
  342.                       
  343.                     //如果太长,拆分  
  344.                     int k= str.length()-1;  
  345.                     while(width - drawedWidth < obWidth)  
  346.                     {  
  347.                         obWidth = paint.measureText(str.substring(0,k--));  
  348.                     }  
  349.                     if(k < str.length()-1)  
  350.                     {  
  351.                         splitFlag = true;  
  352.                         SpanObject so1 = new SpanObject();  
  353.                         so1.start = ((SpanObject) ob).start;  
  354.                         so1.end = so1.start + k;  
  355.                         so1.source = str.substring(0,k+1);  
  356.                         so1.span = ((SpanObject) ob).span;  
  357.                           
  358.                         SpanObject so2 = new SpanObject();  
  359.                         so2.start =  so1.end;  
  360.                         so2.end = ((SpanObject) ob).end;  
  361.                         so2.source = str.substring(k+1,str.length());  
  362.                         so2.span = ((SpanObject) ob).span;  
  363.                           
  364.                         ob = so1;  
  365.                         obList.set(i,so2);  
  366.                         i--;  
  367.                     }  
  368.                 }//做字符串处理  
  369.                 else  
  370.                 {  
  371.                     String str = ((SpanObject) ob).source.toString();  
  372.                     obWidth = paint.measureText(str);  
  373.                     obHeight = textSize;  
  374.                 }  
  375.             }  
  376.   
  377.             //这一行满了,存入contentList,新起一行  
  378.             if (width - drawedWidth < obWidth || splitFlag)  
  379.             {  
  380.                 splitFlag = false;  
  381.                 contentList.add(line);  
  382.   
  383.                 if (drawedWidth > lineWidthMax)  
  384.                 {  
  385.                     lineWidthMax = drawedWidth;  
  386.                 }  
  387.                 drawedWidth = 0;  
  388.                 height += line.height + lineSpacing;  
  389.   
  390.                 lineHeight = obHeight;  
  391.   
  392.                 line = new LINE();  
  393.             }  
  394.   
  395.             drawedWidth += obWidth;  
  396.   
  397.             if (ob instanceof String && line.line.size() > 0 && (line.line.get(line.line.size() - 1instanceof String))  
  398.             {  
  399.                 int size = line.line.size();  
  400.                 sb = new StringBuilder();  
  401.                 sb.append(line.line.get(size - 1));  
  402.                 sb.append(ob);  
  403.                 ob = sb.toString();  
  404.                 obWidth = obWidth + line.widthList.get(size - 1);  
  405.                 line.line.set(size - 1, ob);  
  406.                 line.widthList.set(size - 1, (int) obWidth);  
  407.                 line.height = (int) lineHeight;  
  408.   
  409.             }  
  410.             else  
  411.             {  
  412.                 line.line.add(ob);  
  413.                 line.widthList.add((int) obWidth);  
  414.                 line.height = (int) lineHeight;  
  415.             }  
  416.   
  417.         }  
  418.           
  419.         if (drawedWidth > lineWidthMax)  
  420.         {  
  421.             lineWidthMax = drawedWidth;  
  422.         }  
  423.           
  424.         if (line != null && line.line.size() > 0)  
  425.         {  
  426.             contentList.add(line);  
  427.             height += lineHeight + lineSpacing;  
  428.         }  
  429.         if (contentList.size() <= 1)  
  430.         {  
  431.             oneLineWidth = (int) drawedWidth + leftPadding + rightPadding;  
  432.             height = lineSpacing + lineHeight + lineSpacing;  
  433.         }  
  434.   
  435.         cacheData(width, (int) height);  
  436.         return (int) height;  
  437.     }  
  438.   
  439.     /** 
  440.      * 获取缓存的测量数据,避免多次重复测量 
  441.      * @param text 
  442.      * @param width 
  443.      * @return height 
  444.      */  
  445.     @SuppressWarnings("unchecked")  
  446.     private int getCachedData(String text, int width)  
  447.     {  
  448.         SoftReference<MeasuredData> cache = measuredData.get(text);  
  449.         if (cache == null)  
  450.             return -1;  
  451.         MeasuredData md = cache.get();  
  452.         if (md != null && md.textSize == this.getTextSize() && width == md.width)  
  453.         {  
  454.             lineWidthMax = md.lineWidthMax;  
  455.             contentList = (ArrayList<LINE>) md.contentList.clone();  
  456.             oneLineWidth = md.oneLineWidth;  
  457.   
  458.             StringBuilder sb = new StringBuilder();  
  459.             for (int i = 0; i < contentList.size(); i++)  
  460.             {  
  461.                 LINE line = contentList.get(i);  
  462.                 sb.append(line.toString());  
  463.             }  
  464.             return md.measuredHeight;  
  465.         }  
  466.         else  
  467.             return -1;  
  468.     }  
  469.   
  470.     /** 
  471.      * 缓存已测量的数据 
  472.      * @param width 
  473.      * @param height 
  474.      */  
  475.     @SuppressWarnings("unchecked")  
  476.     private void cacheData(int width, int height)  
  477.     {  
  478.         MeasuredData md = new MeasuredData();  
  479.         md.contentList = (ArrayList<LINE>) contentList.clone();  
  480.         md.textSize = this.getTextSize();  
  481.         md.lineWidthMax = lineWidthMax;  
  482.         md.oneLineWidth = oneLineWidth;  
  483.         md.measuredHeight = height;  
  484.         md.width = width;  
  485.         md.hashIndex = ++hashIndex;  
  486.   
  487.         StringBuilder sb = new StringBuilder();  
  488.         for (int i = 0; i < contentList.size(); i++)  
  489.         {  
  490.             LINE line = contentList.get(i);  
  491.             sb.append(line.toString());  
  492.         }  
  493.   
  494.         SoftReference<MeasuredData> cache = new SoftReference<MeasuredData>(md);  
  495.         measuredData.put(text.toString(), cache);  
  496.     }  
  497.   
  498.     /** 
  499.      * 用本函数代替{@link #setText(CharSequence)} 
  500.      * @param cs 
  501.      */  
  502.     public void setMText(CharSequence cs)  
  503.     {  
  504.         text = cs;  
  505.   
  506.         obList.clear();  
  507.   
  508.         ArrayList<SpanObject> isList = new ArrayList<MTextView.SpanObject>();  
  509.         useDefault = false;  
  510.         if (cs instanceof SpannableString)  
  511.         {  
  512.             SpannableString ss = (SpannableString) cs;  
  513.             CharacterStyle[] spans = ss.getSpans(0, ss.length(), CharacterStyle.class);  
  514.             for (int i = 0; i < spans.length; i++)  
  515.             {  
  516.                   
  517.                 int s = ss.getSpanStart(spans[i]);  
  518.                 int e = ss.getSpanEnd(spans[i]);  
  519.                 SpanObject iS = new SpanObject();  
  520.                 iS.span = spans[i];  
  521.                 iS.start = s;  
  522.                 iS.end = e;  
  523.                 iS.source = ss.subSequence(s, e);  
  524.                 isList.add(iS);  
  525.             }  
  526.         }  
  527.           
  528.         //对span进行排序,以免不同种类的span位置错乱  
  529.         SpanObject[] spanArray = new SpanObject[isList.size()];  
  530.         isList.toArray(spanArray);  
  531.         Arrays.sort(spanArray,0,spanArray.length,new SpanObjectComparator());  
  532.         isList.clear();  
  533.         for(int i=0;i<spanArray.length;i++)  
  534.         {  
  535.             isList.add(spanArray[i]);  
  536.         }  
  537.           
  538.         String str = cs.toString();  
  539.   
  540.         for (int i = 0, j = 0; i < cs.length(); )  
  541.         {  
  542.             if (j < isList.size())  
  543.             {  
  544.                 SpanObject is = isList.get(j);  
  545.                 if (i < is.start)  
  546.                 {  
  547.                     Integer cp = str.codePointAt(i);  
  548.                     //支持增补字符  
  549.                     if (Character.isSupplementaryCodePoint(cp))  
  550.                     {  
  551.                         i += 2;  
  552.                     }  
  553.                     else  
  554.                     {  
  555.                         i++;  
  556.                     }  
  557.   
  558.                     obList.add(new String(Character.toChars(cp)));  
  559.   
  560.                 }  
  561.                 else if (i >= is.start)  
  562.                 {  
  563.                     obList.add(is);  
  564.                     j++;  
  565.                     i = is.end;  
  566.                 }  
  567.             }  
  568.             else  
  569.             {  
  570.                 Integer cp = str.codePointAt(i);  
  571.                 if (Character.isSupplementaryCodePoint(cp))  
  572.                 {  
  573.                     i += 2;  
  574.                 }  
  575.                 else  
  576.                 {  
  577.                     i++;  
  578.                 }  
  579.   
  580.                 obList.add(new String(Character.toChars(cp)));  
  581.             }  
  582.         }  
  583.   
  584.         requestLayout();  
  585.     }  
  586.   
  587.     public void setUseDefault(boolean useDefault)  
  588.     {  
  589.         this.useDefault = useDefault;  
  590.         if (useDefault)  
  591.         {  
  592.             this.setText(text);  
  593.             this.setTextColor(textColor);  
  594.         }  
  595.     }  
  596.     /** 
  597.      * 设置行距 
  598.      * @param lineSpacingDP 行距,单位dp 
  599.      */  
  600.     public void setLineSpacingDP(int lineSpacingDP)  
  601.     {  
  602.         this.lineSpacingDP = lineSpacingDP;  
  603.         lineSpacing = dip2px(context, lineSpacingDP);  
  604.     }  
  605.     /** 
  606.      * 获取行距 
  607.      * @return 行距,单位dp 
  608.      */  
  609.     public int getLineSpacingDP()  
  610.     {  
  611.         return lineSpacingDP;  
  612.     }  
  613.     /** 
  614.      * @author huangwei 
  615.      * @version SocialClient 1.2.0 
  616.      * @功能: 存储Span对象及相关信息 
  617.      * @2014年5月27日 
  618.      * @下午5:21:37 
  619.      */  
  620.     class SpanObject  
  621.     {  
  622.         public Object span;  
  623.         public int start;  
  624.         public int end;  
  625.         public CharSequence source;  
  626.     }  
  627.     /** 
  628.      * @功能: 对SpanObject进行排序 
  629.      * @author huangwei 
  630.      * @2014年6月4日 
  631.      * @下午5:21:30 
  632.      * @version SocialClient 1.2.0 
  633.      */  
  634.     class SpanObjectComparator implements Comparator<SpanObject>  
  635.     {  
  636.         @Override  
  637.         public int compare(SpanObject lhs, SpanObject rhs)  
  638.         {  
  639.               
  640.             return lhs.start - rhs.start;  
  641.         }  
  642.           
  643.     }  
  644.     /** 
  645.      * @author huangwei 
  646.      * @version SocialClient 1.2.0 
  647.      * @功能: 存储测量好的一行数据 
  648.      * @2014年5月27日 
  649.      * @下午5:22:12 
  650.      */  
  651.     class LINE  
  652.     {  
  653.         public ArrayList<Object> line = new ArrayList<Object>();  
  654.         public ArrayList<Integer> widthList = new ArrayList<Integer>();  
  655.         public int height;  
  656.   
  657.         @Override  
  658.         public String toString()  
  659.         {  
  660.             StringBuilder sb = new StringBuilder("height:" + height + "   ");  
  661.             for (int i = 0; i < line.size(); i++)  
  662.             {  
  663.                 sb.append(line.get(i) + ":" + widthList.get(i));  
  664.             }  
  665.             return sb.toString();  
  666.         }  
  667.   
  668.     }  
  669.   
  670.     /** 
  671.      * @author huangwei 
  672.      * @version SocialClient 1.2.0 
  673.      * @功能: 缓存的数据 
  674.      * @2014年5月27日 
  675.      * @下午5:22:25 
  676.      */  
  677.     class MeasuredData  
  678.     {  
  679.         public int measuredHeight;  
  680.         public float textSize;  
  681.         public int width;  
  682.         public float lineWidthMax;  
  683.         public int oneLineWidth;  
  684.         public int hashIndex;  
  685.         ArrayList<LINE> contentList;  
  686.   
  687.     }  


为方便在ListView中使用(ListView反复上下滑动会多次重新onMeasure),加了缓存,相同的情况下可以不用重复在测量一次。

对于SpannableString,只支持了ImageSpan,有其它需要者可自行扩展

Demo:http://download.csdn.net/detail/yellowcath/7421147

或:

https://github.com/yellowcath/MTextView.git (2014/6/4 更新 添加对BackGroundColorSpan的支持,修复一个会导致最后一行最后一个图形显示不全的bug)


备注:android上的textview显示文本不换行的问题确实不美观,这篇文章可以作为参考,github上还有类似的工程:android-justifiedtextview

你可能感兴趣的:(Android 自绘TextView解决提前换行问题,支持图文混排)