android—圖文垂直居中 TextView+SpannableString

自我感覺做什么事情都是事倍功半铃辖,同樣性格還是丟三落四的人跨蟹。記錄每一次解決問題的思路經(jīng)過,以供自我學(xué)

最終效果

前幾天讓做一個(gè)效果如上圖搔谴,于是引發(fā)了一些列的思路風(fēng)暴:
(思路1)TextView+Html的形式:html在網(wǎng)頁(yè)實(shí)現(xiàn)很常見的干旁,所以不免第一個(gè)反應(yīng)就是用html驶沼。于是讓前端哥們寫了一段html文本,但是當(dāng)我用著個(gè)文本顯示的時(shí)候發(fā)現(xiàn)沒有效果争群,于是開始想是不是因?yàn)楦鐐冇玫腃SS3.0回怜,html5的原因(因?yàn)榭赡苁謾C(jī)的TextView不支持,所以可能是這原因)换薄,然后去網(wǎng)上搜索看TextView都支持什么html標(biāo)簽玉雾,最后發(fā)現(xiàn)TextView支持有限的Html標(biāo)簽,其中知識(shí)一些簡(jiǎn)單的字體轻要,顏色复旬,背景,還不支持CSS(更不用說CSS3.0了)

思路1總結(jié):整個(gè)過程耗費(fèi)了半個(gè)下午冲泥,其中還包括1一個(gè)人情(前端哥們的幫忙) 驹碍。查詢TextView具體支持標(biāo)簽在Html.from("")的方法中查找
知識(shí)點(diǎn)總結(jié):TextView支持的html很是有限的,關(guān)于字體的樣式還是用自個(gè)的標(biāo)簽<font>凡恍。而且最后html會(huì)被轉(zhuǎn)換span的形式

(思路2)ImageScpan+自定義drawable方式:textview+html不行,那么志秃,只能用span。為什么使用ImageSpan原因:1嚼酝,因?yàn)榭梢园选胺?wù)中”這個(gè)塊當(dāng)成圖片浮还。如果用ImageScpan實(shí)現(xiàn)了的話,后期可以隨便換成任何圖片革半。2碑定。不使用ImageSpan的話流码,只能使用backgroundscpan,relativespan和字體顏色span等至少三個(gè)集合又官,有點(diǎn)多了感覺,最重要的是Imagespan是可以到行尾部換行了(解釋:如果行尾的預(yù)留的寬度不夠的話漫试,會(huì)另起一行六敬。所選文字對(duì)于一個(gè)圖片塊),不知道其他的行不行(解釋:字面意思backgroundspan只是改變所選文章的背景色驾荣,所針對(duì)的文字還是一個(gè)對(duì)一個(gè)外构。另外兩個(gè)span一樣的)普泡。使用imagespan+圖片的形式是合理的選擇,既然這個(gè)形式的話那么imagespan+自定義的drawable是最好的思路出現(xiàn)了审编,這里是因?yàn)樽远xdrawable可以繪制任何圖片撼班。于是要自個(gè)實(shí)現(xiàn)一個(gè)drawable和系統(tǒng)的imagespan組裝這個(gè)效果。到這里以為終于可以了垒酬,走幾步才發(fā)現(xiàn)砰嘁,嘿嘿...

系統(tǒng)的Imagescpan不行,不能和文字垂直居中勘究,并且當(dāng)所使用圖片高度大于文字的ascent(矮湘。好像是這個(gè)。 )時(shí)口糕,改行的行高使用會(huì)加上一定高度缅阳。于是上網(wǎng)搜索垂直居中ImageScpan 。

思路2總結(jié):整個(gè)過程進(jìn)展也算合理景描。最后的結(jié)果是:搜索的垂直居中imagescpan+自定義drawable十办。(出錯(cuò)了,為什么不直接寫一個(gè)自定義自個(gè)的Imagescpan呢)工作量相對(duì)多超棺,自定義兩個(gè)東西
知識(shí)點(diǎn)總結(jié):
1橘洞,系統(tǒng)的Imagescpan不行,不能和文字垂直居中说搅,并且當(dāng)所使用圖片高度大于文字的ascent(炸枣。好像是這個(gè)。 )時(shí)弄唧,改行的行高使用會(huì)加上一定高度适肠。于是上網(wǎng)搜索垂直居中ImageScpan 。
2候引,牽扯到了drawable自定義 侯养,了解到當(dāng)drawable.draw(canvas)之前 drawable.getbouds返回的區(qū)域必須是有個(gè)有空間的區(qū)域。不能是高為0澄干,寬為0逛揩,這樣的話只會(huì)看不到
3,中間搜索到了一個(gè)大神寫的垂直居中的ImageSpan

(思路3) 自定義自個(gè)的ImageSpan:直接自定義自個(gè)的span麸俘,拋棄了思路2還用自個(gè)寫自定義drawable辩稽,顯然這個(gè)是不錯(cuò)的。直接朝這個(gè)方向前進(jìn)吧4用摹逞泄!最后完成了效果
思路3總結(jié):canvas.drawline的時(shí)候水平線應(yīng)該是字體的baseline的位置。

針對(duì)這個(gè)問題最終總結(jié):
1, 是因?yàn)樽詡€(gè)不知道textview支持多少html標(biāo)簽喷众,所以有了思路1各谚。途中得到的戰(zhàn)果
TextView支持的html很是有限的,關(guān)于字體的樣式還是用自個(gè)的標(biāo)簽到千。而且最后html會(huì)被轉(zhuǎn)換span的形式

2昌渤, 為什么會(huì)出現(xiàn)思路2的情況,有兩個(gè)需要自定義的類憔四,是因?yàn)楫?dāng)時(shí)大腦亂糾結(jié)這個(gè)問題太長(zhǎng)時(shí)間了愈涩。沒想過直接二合一直接自定義一個(gè) 途中得到的戰(zhàn)果

  • 系統(tǒng)的Imagescpan不行,不能和文字垂直居中加矛,并且當(dāng)所使用圖片高度大于文字的ascent(履婉。好像是這個(gè)。 )時(shí)斟览,改行的行高使用會(huì)加上一定高度毁腿。于是上網(wǎng)搜索垂直居中ImageScpan 。
  • 牽扯到了drawable自定義 苛茂,了解到當(dāng)drawable.draw(canvas)之前 drawable.getbouds返回的區(qū)域必須是有個(gè)有空間的區(qū)域已烤。不能是高為0,寬為0妓羊,這樣的話只會(huì)看不到
  • 中間搜索到了一個(gè)大神寫的垂直居中的ImageSpan
  • 在xml中設(shè)置textview的行高不會(huì)體現(xiàn)的設(shè)置字體高度(原以為會(huì)體現(xiàn)到字體 dscent )胯究,只是體現(xiàn)到行與行之間的距離上
獻(xiàn)上最后的兩個(gè)重要的ImageSpan

網(wǎng)上搜索的某個(gè)大神:

public class VerticalImageSpan extends ImageSpan {                            //根據(jù)圖片調(diào)整字體,來是適應(yīng)圖片的高度
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) {  //設(shè)置圖片塊的寬度
Drawable drawable = getDrawable();
Rect rect = drawable.getBounds();                    //注意點(diǎn)躁绸,這個(gè)rect應(yīng)該是有效的空間 高度為0裕循,寬度為0 drawable就繪制不出來,在這個(gè)地方是用
if(fontMetricsInt !=null) {                                    //來調(diào)整字體高度的净刮,因?yàn)橐屛谋拘羞m應(yīng)圖片高度
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                                                                        //文本的基準(zhǔn)線
*@parambottomthe bottom of the line                                                        //文本所在改行的底部 及下行的頂部剥哑,xml文件中的設(shè)置的行間距會(huì)直接影響 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();                                            //注意點(diǎn),這個(gè)rect應(yīng)該是有效的空間 高度為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();
}
}

這個(gè)是我最終的圖文居中span,如下:

public class CustomSpan extends ImageSpan {         //圖片適應(yīng)文本行高度
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) {            //設(shè)置圖片塊的寬度
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                                                                        //文本的基準(zhǔn)線

*@parambottomthe bottom of the line                                                        //文本所在改行的底部 及下行的頂部,xml文件中的設(shè)置的行間距

會(huì)直接影響 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);//移動(dòng)到該塊的原點(diǎn)

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);    //移動(dòng)的值是相對(duì)的暑认。移動(dòng)到“塊”中字體“服務(wù)中”baseline

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

paint.setColor(oldTextColor);

paint.setTextSize(oldTextSize);

canvas.restore();

}

}


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末困介,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蘸际,更是在濱河造成了極大的恐慌座哩,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捡鱼,死亡現(xiàn)場(chǎng)離奇詭異八回,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)驾诈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門缠诅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乍迄,你說我怎么就攤上這事管引。” “怎么了闯两?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵褥伴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我漾狼,道長(zhǎng)重慢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任逊躁,我火速辦了婚禮似踱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘稽煤。我一直安慰自己核芽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布酵熙。 她就那樣靜靜地躺著轧简,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匾二。 梳的紋絲不亂的頭發(fā)上哮独,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音察藐,去河邊找鬼借嗽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛转培,可吹牛的內(nèi)容都是我干的恶导。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼浸须,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼惨寿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起删窒,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤裂垦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后肌索,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蕉拢,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晕换。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片午乓。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖闸准,靈堂內(nèi)的尸體忽然破棺而出益愈,到底是詐尸還是另有隱情,我是刑警寧澤夷家,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布蒸其,位于F島的核電站,受9級(jí)特大地震影響库快,放射性物質(zhì)發(fā)生泄漏摸袁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一义屏、第九天 我趴在偏房一處隱蔽的房頂上張望但惶。 院中可真熱鬧,春花似錦湿蛔、人聲如沸膀曾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)添谊。三九已至,卻和暖如春察迟,著一層夾襖步出監(jiān)牢的瞬間斩狱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工扎瓶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留所踊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓概荷,卻偏偏與公主長(zhǎng)得像秕岛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子误证,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,139評(píng)論 25 707
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程继薛,因...
    小菜c閱讀 6,419評(píng)論 0 17
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 46,761評(píng)論 22 665
  • 旗袍是注定來隱匿女人的那些 豐盈和美麗的 緊扣的衣領(lǐng)和高高的開叉 恰到好處的隱藏和暴露 喚醒和勾起男人無限的遐想 ...
    嘉溫閱讀 398評(píng)論 1 12
  • 幽壑 2017.10 在這個(gè)世界上,沒有什么東西是可以比齊的...
    幽壑閱讀 462評(píng)論 1 1