日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

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

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

在客戶(hù)端開(kāi)發(fā)中,我們往往需要對(duì)一個(gè)TextView的文字的部分內(nèi)容進(jìn)行特殊化處理,比如加粗、改變顏色、加鏈接、下劃線等。iOS為我們提供了AttributedString,而Android則提供了SpannableString。今天我們就來(lái)看看SpannableString的主角span。

在Android的android.text.style包下為我們提供了各種各樣的span,如:ForegroundColorSpan、ImageSpan、ClickableSpan等等。網(wǎng)上已經(jīng)有著很多使用這些span的教程了,所以沒(méi)必要在這里繼續(xù)探討這些基礎(chǔ)使用了。今天主要分析的是一個(gè)可以高度自定義化的span:ReplacementSpan。

為何要自定義化?這是因?yàn)樵趯?shí)際需求上,已存在的組件很多時(shí)候不能滿足設(shè)計(jì)的需求。比如我們需要把TextView中一些詞匯變?yōu)閹A角標(biāo)簽的tag;又比如我們要給一段文字分段,但是段間距要精確到像素而不能簡(jiǎn)簡(jiǎn)單單用\n解決。遇到這種需求時(shí),當(dāng)然我們可以把它拆分成很多個(gè)子view去完成,但是我們也可以選擇優(yōu)雅的用自定義span去完成。

為何ReplacementSpan? ReplacementSpan是系統(tǒng)提供給我們的一個(gè)抽象類(lèi)。通過(guò)名字我們可以知道其實(shí)用于是用于替換。指示我們可以把文本的某一部分替換成我們想要的內(nèi)容。這也許是我們想要的。

Relpacement的定義很簡(jiǎn)單:

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) { }

}

我們?cè)诶^承它的時(shí)候,需要實(shí)現(xiàn)兩個(gè)方法getSize和draw。通過(guò)方法名,我們也許能夠知道其作用:getSize用于確定span的大小,draw用于繪制我們想要的內(nèi)容。

但是問(wèn)題來(lái)了,這些方法的傳參是什么?為何getSize只返回了一個(gè)int值?

了解了這兩個(gè)問(wèn)題,就基本弄懂了自定義span。來(lái)回答這兩個(gè)問(wèn)題前,我們首先要明確的一件事情是:span是用于SpannableString中,并且最終被用于TextView中。所以在定義span時(shí),我們的大小、繪制內(nèi)容都應(yīng)該依賴(lài)于使用時(shí)的環(huán)境。我們假設(shè)自定義span使用的環(huán)境為A,那么A將包換一些信息,例如:baseline、Paint、FontMetricsInt等信息。

那我們現(xiàn)在來(lái)看看getSize方法。getSize的返回值是int,其實(shí)這個(gè)值指的是自定義span的寬度,那它的高度呢?其實(shí)高度是已知的,那就是外界環(huán)境A帶來(lái)的字的高度。但我某些情況我們希望改變span的高度,我們?cè)撛趺醋瞿?#xff1f; 如果對(duì)Android上字體繪制有一定了解的同學(xué)會(huì)知道,一個(gè)字的高度取決于繪制這個(gè)子的Paint.FontMetricsInt,而Paint.FontMetricsInt又有asent和desent來(lái)表示字體baseline以上的高度和以下的高度。如果對(duì)字體繪制與測(cè)量不清楚的同學(xué)可以看看這篇博文:http://mikewang.blog.51cto.com/3826268/871765 。 而getSize方法的參數(shù)中有Paint.FontMetricsInt,那我們是否就可以通過(guò)改變傳入的Paint.FontMetricsInt的asent和desent來(lái)達(dá)到改變高度的目的呢?答案是可行的。我們可以來(lái)看看官方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之后那我們來(lái)看看如何繪制了。draw方法有一堆的參數(shù),但源碼并沒(méi)有給出各個(gè)參數(shù)的作用,這里我直接指出:

CharSequence text, int start, int end: 這三個(gè)用來(lái)指示想要替換的原文本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; //視覺(jué)感覺(jué)偏下了,往上一點(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的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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