日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

android 自定义span_教你自定义android中span

發(fā)布時(shí)間:2025/3/20 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 自定义span_教你自定义android中span 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在客戶端開發(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。