前一段時(shí)間一直在做富文本展示和文本處理,主要用到了Html.fromHtml()實(shí)現(xiàn)加載網(wǎng)頁(yè)盟戏,但實(shí)現(xiàn)整段文本的某些特殊如個(gè)別文字的點(diǎn)擊,改背景色邮旷、前景色等效果婶肩,就用到了我們今天要用到的Span這個(gè)類律歼。
關(guān)于加載網(wǎng)頁(yè)或個(gè)別文字點(diǎn)擊效果险毁,可以閱讀我之前寫的一篇文章——用TextView實(shí)現(xiàn)富文本展示畔况,點(diǎn)擊斷句和語(yǔ)音播報(bào)问窃。
您也關(guān)注:
- https://github.com/shuaijia/SpanString
- https://github.com/shuaijia/RichTextView
- 我的微信公眾號(hào)——安卓干貨營(yíng)
那接下來(lái)就先看下效果圖
今天會(huì)簡(jiǎn)單介紹幾個(gè)Span的基本用法,也會(huì)分享一些比較酷炫的使用方法:
- 設(shè)置字體顏色
- 改變字體背景色
- 給文本添加下劃線
- 給文本加邊框
- 彩虹色文字
- 彩虹色字體漸變動(dòng)畫
- 打字效果展示文本
1覆积、設(shè)置字體顏色
/**
* 設(shè)置不同顏色文字
*/
private void setForegroundColor() {
SpannableString spannableString = new SpannableString(
"我愛北京天安門,天安門上太陽(yáng)升 我愛北京天安門庵朝,天安門上太陽(yáng)升");
spannableString.setSpan(new ForegroundColorSpan(Color.RED), 0, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv_diff_color.setText(spannableString);
}
2椎瘟、改變字體背景色
[圖片上傳失敗...(image-98d6e5-1512636199700)]
/**
* 設(shè)置背景色
*/
private void setBackgroundColor() {
SpannableString spannableString = new SpannableString(
"我愛北京天安門侄旬,天安門上太陽(yáng)升 我愛北京天安門宣羊,天安門上太陽(yáng)升");
spannableString.setSpan(new BackgroundColorSpan(Color.RED), 0, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv_bg_color.setText(spannableString);
}
3、給文本添加下劃線
/**
* 設(shè)置超鏈接
*/
private void setLink() {
SpannableString spannableString = new SpannableString(
"我愛北京天安門,天安門上太陽(yáng)升 我愛北京天安門澈缺,天安門上太陽(yáng)升");
//設(shè)置下劃線
spannableString.setSpan(new UnderlineSpan(), 0, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv_link.setText(spannableString);
}
4炕婶、給文本加邊框
這種效果就不再是簡(jiǎn)單的直接使用系統(tǒng)提供的Span類就可以了项滑,需要我們自定義:
public class FrameSpan extends ReplacementSpan {
private final Paint mPaint;
private int mWidth;
public FrameSpan() {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.RED);
mPaint.setAntiAlias(true);
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
//return text with relative to the Paint
mWidth = (int) paint.measureText(text, start, end);
return mWidth;
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
//draw the frame with custom Paint
canvas.drawRect(x, top, x + mWidth, bottom, mPaint);
canvas.drawText(text, start, end, x, y, paint);
}
}
類似于自定義View枪狂,最重要的是在初始化畫筆州疾、獲取繪制區(qū)域大小皇拣、在draw中繪制矩形邊框氧急。
這里就不再重復(fù)累贅了。
然后和之前類似毒姨,使用它:
/**
* 給文字加邊框
*/
private void addBox() {
SpannableString spannableString = new SpannableString(
"我愛北京天安門,天安門上太陽(yáng)升 我愛北京天安門嵌纲,天安門上太陽(yáng)升");
spannableString.setSpan(new FrameSpan(), 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv_biankuang.setText(spannableString);
}
5疹瘦、彩虹色文字
自定義Span:
public class RainbowSpan extends CharacterStyle implements UpdateAppearance {
private int[] colors;
public RainbowSpan(Context context) {
colors = context.getResources().getIntArray(R.array.splash_bg);
}
@Override
public void updateDrawState(TextPaint paint) {
paint.setStyle(Paint.Style.FILL);
Shader shader = new LinearGradient(0, 0, 0, paint.getTextSize() * colors.length, colors, null,
Shader.TileMode.MIRROR);
Matrix matrix = new Matrix();
matrix.setRotate(90);
shader.setLocalMatrix(matrix);
paint.setShader(shader);
}
}
其中CharSequence是一組可讀的Char序列酣栈,提供了操作Char序列的接口汹押,是所有Span類的根父類窖维。
使用Shader進(jìn)行著色渲染妙痹,LinearGradient是線性漸變琳轿,Gradient是基于Shader類崭篡,所以我們通過(guò)Paint的setShader方法來(lái)設(shè)置這個(gè)漸變.
LinearGradient參數(shù)含義:
- X0: 漸變起初點(diǎn)坐標(biāo)x位置
- y0: 漸變起初點(diǎn)坐標(biāo)y位置
- x1: 漸變終點(diǎn)坐標(biāo)x位置
- y1: 漸變終點(diǎn)坐標(biāo)y位置
- color0: 漸變開始顏色
- color1: 漸變結(jié)束顏色
- tile: 平鋪方式
然后使用
/**
* 設(shè)置彩色字體
*/
private void setColofulText() {
SpannableString spannableString = new SpannableString(
"我愛北京天安門琉闪,天安門上太陽(yáng)升 我愛北京天安門颠毙,天安門上太陽(yáng)升");
spannableString.setSpan(new RainbowSpan(this), 0, 15, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv_color.setText(spannableString);
}
6吟秩、彩虹色文字動(dòng)起來(lái)
和上一步相比闹伪,不同的是,需實(shí)時(shí)更新杀怠、重繪彩色赔退,所以硕旗,自定義的Span類相比于上一個(gè)RainbowSpan來(lái)說(shuō)漆枚,主要有以下不同:
- 聲明變量—— 橫向漸變的百分比translateXPercentage,并設(shè)置對(duì)應(yīng)get墙基、set方法软族;
- 在updateDrawState中使用matrix.postTranslate進(jìn)行變化;
- postTranslate是指在setRotate后平移残制。
public class AnimatedColorSpan extends CharacterStyle implements UpdateAppearance {
private final int[] colors;
private Shader shader = null;
private Matrix matrix = new Matrix();
private float translateXPercentage = 0;
public AnimatedColorSpan(Context context) {
colors = context.getResources().getIntArray(R.array.splash_bg);
}
public void setTranslateXPercentage(float percentage) {
translateXPercentage = percentage;
}
public float getTranslateXPercentage() {
return translateXPercentage;
}
@Override
public void updateDrawState(TextPaint paint) {
paint.setStyle(Paint.Style.FILL);
float width = paint.getTextSize() * colors.length;
if (shader == null) {
shader = new LinearGradient(0, 0, 0, width, colors, null,
Shader.TileMode.MIRROR);
}
matrix.reset();
matrix.setRotate(90);
matrix.postTranslate(width * translateXPercentage, 0);
shader.setLocalMatrix(matrix);
paint.setShader(shader);
}
}
然后使用:
/**
* 設(shè)置彩色動(dòng)畫
*/
private void setColofulAnimText() {
final SpannableString spannableString = new SpannableString(
"我愛北京天安門立砸,天安門上太陽(yáng)升 我愛北京天安門,天安門上太陽(yáng)升");
AnimatedColorSpan span = new AnimatedColorSpan(this);
spannableString.setSpan(span, 0, 15, 0);
// 設(shè)置動(dòng)畫
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(
span, ANIMATED_COLOR_SPAN_FLOAT_PROPERTY, 0, 100);
objectAnimator.setEvaluator(new FloatEvaluator());
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
tv_color_anim.setText(spannableString);
}
});
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.setDuration(DateUtils.MINUTE_IN_MILLIS * 2);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.start();
}
大家可能注意到了初茶,設(shè)置動(dòng)畫時(shí)用到了動(dòng)畫屬性變化器:
/**
* 彩色動(dòng)畫 屬性變化器
*/
private static final Property<AnimatedColorSpan, Float> ANIMATED_COLOR_SPAN_FLOAT_PROPERTY
= new Property<AnimatedColorSpan, Float>(Float.class, "ANIMATED_COLOR_SPAN_FLOAT_PROPERTY") {
@Override
public void set(AnimatedColorSpan span, Float value) {
span.setTranslateXPercentage(value);
}
@Override
public Float get(AnimatedColorSpan span) {
return span.getTranslateXPercentage();
}
};
在變化器的對(duì)應(yīng)方法中設(shè)置Span變化的百分比颗祝。
7、文本實(shí)現(xiàn)打字效果
先看看Span的寫法:
public class MutableForegroundColorSpan extends CharacterStyle implements UpdateAppearance {
private int mColor = Color.BLACK;
private int mAlpha = 0 ;
@Override
public void updateDrawState(TextPaint tp) {
tp.setColor(mColor);
tp.setAlpha(mAlpha);
}
public int getColor() {
return mColor;
}
public void setColor(int color) {
this.mColor = color;
}
public void setAlpha(int alpha) {
mAlpha = alpha;
}
}
public class TypeWriterSpanGroup {
private final float mAlpha;
private final ArrayList<MutableForegroundColorSpan> mSpans;
public TypeWriterSpanGroup(float alpha) {
mAlpha = alpha;
mSpans = new ArrayList<MutableForegroundColorSpan>();
}
public void addSpan(MutableForegroundColorSpan span) {
span.setAlpha((int) (mAlpha * 255));
mSpans.add(span);
}
public void setAlpha(float alpha) {
int size = mSpans.size();
float total = 1.0f * size * alpha;
for(int index = 0 ; index < size; index++) {
MutableForegroundColorSpan span = mSpans.get(index);
if(total >= 1.0f) {
span.setAlpha(255);
total -= 1.0f;
} else {
span.setAlpha((int) (total * 255));
total = 0.0f;
}
}
}
public float getAlpha() {
return mAlpha;
}
}
思路是這樣的纺蛆,每打印一個(gè)文字桥氏,都是一個(gè)對(duì)應(yīng)的MutableForegroundColorSpan,要想實(shí)現(xiàn)連續(xù)的打印每個(gè)字,我們需要?jiǎng)?chuàng)建一個(gè)集合來(lái)存放所有得Span蹄梢。
循環(huán)集合中所有的Span幕袱,除了最近一個(gè)打印的字以外涯捻,其他的字設(shè)置為不透明混弥,第一個(gè)跟隨動(dòng)畫進(jìn)行漸變蒿涎。
看下動(dòng)畫的使用:
/**
* 打字效果
*/
private void addTyping() {
String content = "我愛北京天安門,天安門上太陽(yáng)升 我愛北京天安門,天安門上太陽(yáng)升";
final SpannableString spannableString = new SpannableString(content);
// 添加Span
final TypeWriterSpanGroup group = new TypeWriterSpanGroup(0);
for (int index = 0; index <= content.length() - 1; index++) {
MutableForegroundColorSpan span = new MutableForegroundColorSpan();
group.addSpan(span);
spannableString.setSpan(span, index, index + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
// 添加動(dòng)畫
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(group, TYPE_WRITER_GROUP_ALPHA_PROPERTY, 0.0f, 1.0f);
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//refresh
tv_dazi.setText(spannableString);
}
});
objectAnimator.setDuration(5000);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.start();
}
還有變化器:
/**
* 打字動(dòng)畫 屬性變化器
*/
private static final Property<TypeWriterSpanGroup, Float> TYPE_WRITER_GROUP_ALPHA_PROPERTY =
new Property<TypeWriterSpanGroup, Float>(Float.class, "TYPE_WRITER_GROUP_ALPHA_PROPERTY") {
@Override
public void set(TypeWriterSpanGroup spanGroup, Float value) {
spanGroup.setAlpha(value);
}
@Override
public Float get(TypeWriterSpanGroup spanGroup) {
return spanGroup.getAlpha();
}
};
關(guān)于使用Span實(shí)現(xiàn)TextView的幾種效果谚攒,大致就介紹到這,有錯(cuò)誤的地方和不足的地方浪耘,希望大家提出规婆,我們一起進(jìn)步_嗡髓。
更多精彩內(nèi)容,請(qǐng)關(guān)注我的微信公眾號(hào)——Android機(jī)動(dòng)車。