android 自定义span_教你自定义android中span
在客戶端開發(fā)中,我們往往需要對一個(gè)TextView的文字的部分內(nèi)容進(jìn)行特殊化處理,比如加粗、改變顏色、加鏈接、下劃線等。iOS為我們提供了AttributedString,而Android則提供了SpannableString。今天我們就來看看SpannableString的主角span。
在Android的android.text.style包下為我們提供了各種各樣的span,如:ForegroundColorSpan、ImageSpan、ClickableSpan等等。網(wǎng)上已經(jīng)有著很多使用這些span的教程了,所以沒必要在這里繼續(xù)探討這些基礎(chǔ)使用了。今天主要分析的是一個(gè)可以高度自定義化的span:ReplacementSpan。
為何要自定義化?這是因?yàn)樵趯?shí)際需求上,已存在的組件很多時(shí)候不能滿足設(shè)計(jì)的需求。比如我們需要把TextView中一些詞匯變?yōu)閹A角標(biāo)簽的tag;又比如我們要給一段文字分段,但是段間距要精確到像素而不能簡簡單單用\n解決。遇到這種需求時(shí),當(dāng)然我們可以把它拆分成很多個(gè)子view去完成,但是我們也可以選擇優(yōu)雅的用自定義span去完成。
為何ReplacementSpan? ReplacementSpan是系統(tǒng)提供給我們的一個(gè)抽象類。通過名字我們可以知道其實(shí)用于是用于替換。指示我們可以把文本的某一部分替換成我們想要的內(nèi)容。這也許是我們想要的。
Relpacement的定義很簡單:
public abstract class ReplacementSpan extends MetricAffectingSpan {
public abstract int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm);
public abstract void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint);
public void updateMeasureState(TextPaint p) { }
public void updateDrawState(TextPaint ds) { }
}
我們在繼承它的時(shí)候,需要實(shí)現(xiàn)兩個(gè)方法getSize和draw。通過方法名,我們也許能夠知道其作用:getSize用于確定span的大小,draw用于繪制我們想要的內(nèi)容。
但是問題來了,這些方法的傳參是什么?為何getSize只返回了一個(gè)int值?
了解了這兩個(gè)問題,就基本弄懂了自定義span。來回答這兩個(gè)問題前,我們首先要明確的一件事情是:span是用于SpannableString中,并且最終被用于TextView中。所以在定義span時(shí),我們的大小、繪制內(nèi)容都應(yīng)該依賴于使用時(shí)的環(huán)境。我們假設(shè)自定義span使用的環(huán)境為A,那么A將包換一些信息,例如:baseline、Paint、FontMetricsInt等信息。
那我們現(xiàn)在來看看getSize方法。getSize的返回值是int,其實(shí)這個(gè)值指的是自定義span的寬度,那它的高度呢?其實(shí)高度是已知的,那就是外界環(huán)境A帶來的字的高度。但我某些情況我們希望改變span的高度,我們該怎么做呢? 如果對Android上字體繪制有一定了解的同學(xué)會(huì)知道,一個(gè)字的高度取決于繪制這個(gè)子的Paint.FontMetricsInt,而Paint.FontMetricsInt又有asent和desent來表示字體baseline以上的高度和以下的高度。如果對字體繪制與測量不清楚的同學(xué)可以看看這篇博文:http://mikewang.blog.51cto.com/3826268/871765 。 而getSize方法的參數(shù)中有Paint.FontMetricsInt,那我們是否就可以通過改變傳入的Paint.FontMetricsInt的asent和desent來達(dá)到改變高度的目的呢?答案是可行的。我們可以來看看官方ImageSpan(ImageSpan繼承自DynamicDrawableSpan,而DynamicDrawableSpan繼承自Replacement,下面的方法由DynamicDrawableSpan實(shí)現(xiàn)的)的實(shí)現(xiàn):
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();
if (fm != null) {
fm.ascent = -rect.bottom;
fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
}
return rect.right;
}
我們可以看到ImageSpan就是用這種做法改變高度,這也可以代表官方的做法了。
知道size之后那我們來看看如何繪制了。draw方法有一堆的參數(shù),但源碼并沒有給出各個(gè)參數(shù)的作用,這里我直接指出:
CharSequence text, int start, int end: 這三個(gè)用來指示想要替換的原文本text、替換的起始位置start和結(jié)束為止end。
float x: 替換文本的起始坐標(biāo)位置。
int y: 作用環(huán)境A的baseline。
int top, int bottom: 這個(gè)與上一步getSize有關(guān),指示span可繪制區(qū)域的top和bottom。
在知道這些參數(shù)之后,我們就可以根據(jù)業(yè)務(wù)需求實(shí)現(xiàn)想要的span,下面給出自己在業(yè)務(wù)上使用的圓角tag型的span:
public class RadiusBackgroundSpan extends ReplacementSpan {
private int mBgColor;
private int mRadius;
private int mTextColor;
private int mTextSize;
private int mPaddingHorizontal;
private String mText;
private int mMarginLeft;
private int mMarginRight;
public RadiusBackgroundSpan(int bgColor, int radius, int textColor, int textSize, int paddingHorizontal, String text){
mBgColor = bgColor;
mRadius = radius;
mTextColor = textColor;
mTextSize = textSize;
mPaddingHorizontal = paddingHorizontal;
mText = text;
mMarginLeft = UIUtil.dpToPx(4);
mMarginRight = UIUtil.dpToPx(4);
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
paint.setTextSize(mTextSize);
return (int) paint.measureText(mText) + 2*mPaddingHorizontal + mMarginLeft + mMarginRight;
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
final int offsetToTop = UIUtil.dpToPx(2);
paint.setTextSize(mTextSize);
paint.setAntiAlias(true);
RectF rect = new RectF();
rect.left = (int) x + mMarginLeft;
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int marginVertical = (bottom - top - fontMetrics.descent + fontMetrics.top)/2;
rect.top = top + marginVertical - offsetToTop; //視覺感覺偏下了,往上一點(diǎn)點(diǎn)
rect.bottom = bottom - marginVertical;
rect.right = rect.left + (int) paint.measureText(mText) + 2*mPaddingHorizontal;
paint.setColor(mBgColor);
canvas.drawRoundRect(rect, mRadius, mRadius, paint);
paint.setColor(mTextColor);
float fontShouldOffsetTop = ((fontMetrics.descent - fontMetrics.ascent)/2+fontMetrics.ascent)/2 - offsetToTop/2;
canvas.drawText(mText,x+mPaddingHorizontal+mMarginLeft,y + fontShouldOffsetTop,paint);
}
}
總結(jié)
以上是生活随笔為你收集整理的android 自定义span_教你自定义android中span的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle改字体大小_oracle数据
- 下一篇: git 拉取远端仓库_Git : 建立自