android—图文垂直居中 TextView+SpannableString

自我感觉做什么事情都是事倍功半,同样性格还是丢三落四的人。记录每一次解决问题的思路经过,以供自我学

最终效果

前几天让做一个效果如上图,于是引发了一些列的思路风暴:
(思路1) TextView+Html的形式:html在网页实现很常见的,所以不免第一个反应就是用html。于是让前端哥们写了一段html文本,但是当我用着个文本显示的时候发现没有效果,于是开始想是不是因为哥们用的CSS3.0,html5的原因(因为可能手机的TextView不支持,所以可能是这原因),然后去网上搜索看TextView都支持什么html标签,最后发现TextView支持有限的Html标签,其中知识一些简单的字体,颜色,背景,还不支持CSS(更不用说CSS3.0了)

思路1总结:整个过程耗费了半个下午,其中还包括1一个人情(前端哥们的帮忙) 。查询TextView具体支持标签在Html.from("")的方法中查找
知识点总结:TextView支持的html很是有限的,关于字体的样式还是用自个的标签。而且最后html会被转换span的形式

(思路2)ImageScpan+自定义drawable方式:textview+html不行,那么,只能用span。为什么使用ImageSpan原因:1,因为可以把“服务中”这个块当成图片。如果用ImageScpan实现了的话,后期可以随便换成任何图片。2。不使用ImageSpan的话,只能使用backgroundscpan,relativespan和字体颜色span等至少三个集合,有点多了感觉,最重要的是Imagespan是可以到行尾部换行了(解释:如果行尾的预留的宽度不够的话,会另起一行。所选文字对于一个图片块),不知道其他的行不行(解释:字面意思backgroundspan只是改变所选文章的背景色,所针对的文字还是一个对一个。另外两个span一样的)。使用imagespan+图片的形式是合理的选择,既然这个形式的话那么imagespan+自定义的drawable是最好的思路出现了,这里是因为自定义drawable可以绘制任何图片。于是要自个实现一个drawable和系统的imagespan组装这个效果。到这里以为终于可以了,走几步才发现,嘿嘿...

系统的Imagescpan不行,不能和文字垂直居中,并且当所使用图片高度大于文字的ascent(。好像是这个。 )时,改行的行高使用会加上一定高度。于是上网搜索垂直居中ImageScpan 。

思路2总结:整个过程进展也算合理。最后的结果是:搜索的垂直居中imagescpan+自定义drawable。(出错了,为什么不直接写一个自定义自个的Imagescpan呢)工作量相对多,自定义两个东西
知识点总结:
1,系统的Imagescpan不行,不能和文字垂直居中,并且当所使用图片高度大于文字的ascent(。好像是这个。 )时,改行的行高使用会加上一定高度。于是上网搜索垂直居中ImageScpan 。
2,牵扯到了drawable自定义 ,了解到当drawable.draw(canvas)之前 drawable.getbouds返回的区域必须是有个有空间的区域。不能是高为0,宽为0,这样的话只会看不到
3,中间搜索到了一个大神写的垂直居中的ImageSpan

(思路3) 自定义自个的ImageSpan:直接自定义自个的span,抛弃了思路2还用自个写自定义drawable,显然这个是不错的。直接朝这个方向前进吧!!最后完成了效果
思路3总结:canvas.drawline的时候水平线应该是字体的baseline的位置。

针对这个问题最终总结:
1, 是因为自个不知道textview支持多少html标签,所以有了思路1。途中得到的战果
TextView支持的html很是有限的,关于字体的样式还是用自个的标签。而且最后html会被转换span的形式

2, 为什么会出现思路2的情况,有两个需要自定义的类,是因为当时大脑乱纠结这个问题太长时间了。没想过直接二合一直接自定义一个 途中得到的战果

  • 系统的Imagescpan不行,不能和文字垂直居中,并且当所使用图片高度大于文字的ascent(。好像是这个。 )时,改行的行高使用会加上一定高度。于是上网搜索垂直居中ImageScpan 。
  • 牵扯到了drawable自定义 ,了解到当drawable.draw(canvas)之前 drawable.getbouds返回的区域必须是有个有空间的区域。不能是高为0,宽为0,这样的话只会看不到
  • 中间搜索到了一个大神写的垂直居中的ImageSpan
  • 在xml中设置textview的行高不会体现的设置字体高度(原以为会体现到字体 dscent ),只是体现到行与行之间的距离上
献上最后的两个重要的ImageSpan

网上搜索的某个大神:

public class VerticalImageSpan extends ImageSpan {                            //根据图片调整字体,来是适应图片的高度
public VerticalImageSpan(Context context,int drawableid) {                  
super(context,drawableid);
}
/**
* update the text line height
*/
@Override
public int getSize(Paint paint,CharSequence text, intstart, intend, Paint.FontMetricsInt fontMetricsInt) {  //设置图片块的宽度
Drawable drawable = getDrawable();
Rect rect = drawable.getBounds();                    //注意点,这个rect应该是有效的空间 高度为0,宽度为0 drawable就绘制不出来,在这个地方是用
if(fontMetricsInt !=null) {                                    //来调整字体高度的,因为要让文本行适应图片高度
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int fontHeight = fmPaint.descent- fmPaint.ascent;
int drHeight = rect.bottom- rect.top;
int centerY = fmPaint.ascent+ fontHeight /2;
fontMetricsInt.ascent= centerY - drHeight /2;
fontMetricsInt.top= fontMetricsInt.ascent;
fontMetricsInt.bottom= centerY + drHeight /2;
fontMetricsInt.descent= fontMetricsInt.bottom;
}
return rect.right;
}
/**
* see detail message in android.text.TextLine
*
*@paramcanvasthe canvas, can be null if not rendering
*@paramtextthe text to be draw
*@paramstartthe text start position
*@paramendthe text end position
*@paramxthe edge of the replacement closest to the leading margin
*@paramtopthe top of the line                                                              //文本所在改行的顶部
*@paramythe baseline                                                                        //文本的基准线
*@parambottomthe bottom of the line                                                        //文本所在改行的底部 及下行的顶部,xml文件中的设置的行间距会直接影响 bottom到baseline的距离
*@parampaintthe work paint
*/
@Override
public void draw(Canvas canvas,CharSequence text, int start, int end,
float x, int top, inty, int bottom,Paint paint) {
CharSequence targetText=text.subSequence(start,end);
Log.v("文字",targetText.toString());
Drawable drawable = getDrawable();
canvas.save();                                
Rect rect =drawable.getBounds();                                            //注意点,这个rect应该是有效的空间 高度为0,宽度为0 drawable就绘制不出来,
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int fontHeight = fmPaint.descent- fmPaint.ascent;
int centerY = y + fmPaint.descent- fontHeight /2;
int transY = centerY - (rect.bottom- rect.top) /2;
canvas.translate(x,transY);
drawable.draw(canvas);
canvas.restore();
}
}

这个是我最终的图文居中span,如下:

public class CustomSpan extends ImageSpan {         //图片适应文本行高度
int resourceId;
int textColor;
float textRadio;
int marginH;//左右间隔
Rect rect;
Drawable drawable;
publicCustomSpan(Context context, int resourceId, int textColor, float textRadio, int marginH) {
super(context,resourceId);
this.resourceId= resourceId;
drawable= context.getResources().getDrawable(resourceId);
this.textRadio= textRadio;
this.textColor= textColor;
this.marginH= marginH;
}
@Override
public int getSize(Paint paint,CharSequence text, intstart, intend,Paint.FontMetricsInt fm) {            //设置图片块的宽度
CharSequence targetText=text.subSequence(start,end);
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int txtW = (int) Math.ceil(paint.measureText(targetText.toString()));
int fontHeight = fmPaint.descent- fmPaint.ascent;
rect=newRect(0,0,txtW,fontHeight);
return rect.right+2*marginH;
}

/**

* see detail message in android.text.TextLine

*

*@paramcanvasthe canvas, can be null if not rendering

*@paramtextthe text to be draw

*@paramstartthe text start position

*@paramendthe text end position

*@paramxthe edge of the replacement closest to the leading margin

*@paramtopthe top of the line                                                              //文本所在改行的顶部

*@paramythe baseline                                                                        //文本的基准线

*@parambottomthe bottom of the line                                                        //文本所在改行的底部 及下行的顶部,xml文件中的设置的行间距

会直接影响 bottom到baseline的距离

*@parampaintthe work paint                                                                        

*/

@Override

public void draw(Canvas canvas,CharSequence text, int start, int end, float x, int top, int y, int bottom,Paint paint) {

CharSequence targetText=text.subSequence(start,end);

intoldTextColor =paint.getColor();

floatoldTextSize =paint.getTextSize();

//        canvas.drawLine(0,top,400,top,paint);

//        canvas.drawLine(0,y,400,y,paint);

//            canvas.drawLine(0,bottom,400,bottom,paint);

Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();

int oldfontascent = fmPaint.ascent;

paint.setColor(textColor);

paint.setTextSize(oldTextSize*textRadio);

fmPaint = paint.getFontMetricsInt();

int txtW = (int) Math.ceil(paint.measureText(targetText.toString()));

int fontHeight = fmPaint.descent- fmPaint.ascent;

canvas.save();

canvas.translate(marginH+x,y+oldfontascent);//移动到该块的原点

drawable.setBounds(rect);//背景的绘制

drawable.draw(canvas);

Log.e("尺寸",rect.bottom-((rect.bottom-fontHeight))+","+(0-oldfontascent*textRadio));

canvas.translate((rect.right-txtW)/2.0f,rect.bottom/2.f+fontHeight/2-fmPaint.descent);    //移动的值是相对的。移动到“块”中字体“服务中”baseline

canvas.drawText(targetText.toString(),0,0,paint);

paint.setColor(oldTextColor);

paint.setTextSize(oldTextSize);

canvas.restore();

}

}


你可能感兴趣的:(android—图文垂直居中 TextView+SpannableString)