例如
设置ImageSpan来进行图文混排
设置BackgroundColorSpan来改变背景颜色
设置ForegroundColorSpan来改变文字颜色
由于直接设置BackgroundColorSpan背景是直角的,这里讲下如何实现自定义圆角背景,顺便讲下ReplacementSpan这个可高度自定义的span。
首先看下自定义圆角背景的效果
这里的自定义圆角的span其实是继承自ReplacementSpan,看下ReplacementSpan的代码
public abstract class ReplacementSpan extends MetricAffectingSpan {
/**
* Returns the width of the span. Extending classes can set the height of the span by updating
* attributes of {@link android.graphics.Paint.FontMetricsInt}. If the span covers the whole
* text, and the height is not set,
* {@link #draw(Canvas, CharSequence, int, int, float, int, int, int, Paint)} will not be
* called for the span.
*
* @param paint Paint instance.
* @param text Current text.
* @param start Start character index for span.
* @param end End character index for span.
* @param fm Font metrics, can be null.
* @return Width of the span.
*/
public abstract int getSize(@NonNull Paint paint, CharSequence text,
@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@Nullable Paint.FontMetricsInt fm);
/**
* Draws the span into the canvas.
*
* @param canvas Canvas into which the span should be rendered.
* @param text Current text.
* @param start Start character index for span.
* @param end End character index for span.
* @param x Edge of the replacement closest to the leading margin.
* @param top Top of the line.
* @param y Baseline.
* @param bottom Bottom of the line.
* @param paint Paint instance.
*/
public abstract void draw(@NonNull Canvas canvas, CharSequence text,
@IntRange(from = 0) int start, @IntRange(from = 0) int end, float x,
int top, int y, int bottom, @NonNull Paint paint);
/**
* This method does nothing, since ReplacementSpans are measured
* explicitly instead of affecting Paint properties.
*/
public void updateMeasureState(TextPaint p) { }
/**
* This method does nothing, since ReplacementSpans are drawn
* explicitly instead of affecting Paint properties.
*/
public void updateDrawState(TextPaint ds) { }
}
ReplacementSpan是个抽象类,主要提供了两个抽象方法getSize,draw,从注释来看getSize返回当前span需要的宽度,draw用来绘制span到canvas。既然有了canvas我们就可以任意绘制了,所以不限于绘制圆角背景,绘制任何其他的Span都是可以的。
看下ReplacementSpan的官网介绍,直接实现为DynamicDrawableSpan,间接有ImageSpan
有心人可以自己研究下DynamicDrawableSpan对上述两个方法实现和ImageSpan
这里直接说下我的圆角背景的实现,实现思路就是,在getSize方法中计算要加背景的文字的宽度并返回,在draw方法中绘制一个圆角矩形,然后在把文字绘制到圆角矩形上。(因为有canvas所以这里其实也可以做其他的绘制,看开发者的想象力了。)
下边看下代码实现
public class RadiusBackgroundSpan extends ReplacementSpan {
private int mSize;
private int mColor;
private int mRadius;
/**
* @param color 背景颜色
* @param radius 圆角半径
*/
public RadiusBackgroundSpan(int color, int radius) {
mColor = color;
mRadius = radius;
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
mSize = (int) (paint.measureText(text, start, end) + 2 * mRadius);
//mSize就是span的宽度,span有多宽,开发者可以在这里随便定义规则
//我的规则:这里text传入的是SpannableString,start,end对应setSpan方法相关参数
//可以根据传入起始截至位置获得截取文字的宽度,最后加上左右两个圆角的半径得到span宽度
return mSize;
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
int color = paint.getColor();//保存文字颜色
paint.setColor(mColor);//设置背景颜色
paint.setAntiAlias(true);// 设置画笔的锯齿效果
RectF oval = new RectF(x, y + paint.ascent(), x + mSize, y + paint.descent());
//设置文字背景矩形,x为span其实左上角相对整个TextView的x值,y为span左上角相对整个View的y值。paint.ascent()获得文字上边缘,paint.descent()获得文字下边缘
canvas.drawRoundRect(oval, mRadius, mRadius, paint);//绘制圆角矩形,第二个参数是x半径,第三个参数是y半径
paint.setColor(color);//恢复画笔的文字颜色
canvas.drawText(text, start, end, x + mRadius, y, paint);//绘制文字
}
}
1.通过paint的measureText获得文字宽度
2.背景要根据文字的高度绘制,详细了解Paint.FontMetrics的ascent,descent相关属性。http://blog.csdn.net/industriously/article/details/51009274
3.绘制背景是在整个TextView中,所以要加上x,y的偏移。
最后结合ClickableSpan可以实现点击效果,可以实现类似Button与Text混排效果
最后本文主要讲了怎么自定义圆角背景Span,同时讲述了ReplacementSpan这个高度定式化的Span。
demo地址https://github.com/pengyuntao/RadiusBackgroundSpan