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很是有限的,关于字体的样式还是用自个的标签<font>。而且最后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();

}

}


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,135评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,419评论 0 17
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,760评论 22 665
  • 旗袍是注定来隐匿女人的那些 丰盈和美丽的 紧扣的衣领和高高的开叉 恰到好处的隐藏和暴露 唤醒和勾起男人无限的遐想 ...
    嘉温阅读 398评论 1 12
  • 幽壑 2017.10 在这个世界上,没有什么东西是可以比齐的...
    幽壑阅读 462评论 1 1