Android自定义圆角Span背景

简述

在Android开发中,有时候需要对TextView中文字进行特殊化处理,例如给局部文字设置超链接、颜色、字体,背景色等。Android为我们提供了SpannableString来设置富文本,可以通过给SpannableString设置不同的span来改变TextView中文本的显示效果。

例如
设置ImageSpan来进行图文混排
设置BackgroundColorSpan来改变背景颜色
设置ForegroundColorSpan来改变文字颜色
由于直接设置BackgroundColorSpan背景是直角的,这里讲下如何实现自定义圆角背景,顺便讲下ReplacementSpan这个可高度自定义的span。
首先看下自定义圆角背景的效果
Android自定义圆角Span背景_第1张图片

这里的自定义圆角的span其实是继承自ReplacementSpan,看下ReplacementSpan的代码

高度可定制化的Span: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
Android自定义圆角Span背景_第2张图片
有心人可以自己研究下DynamicDrawableSpan对上述两个方法实现和ImageSpan

圆角背景RadiusBackgroundSpan实现

这里直接说下我的圆角背景的实现,实现思路就是,在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

你可能感兴趣的:(Android,自定义控件)