TextView 局部文本樣式設置之三:採用建造者模式抽取通用的 ReplacementSpan

package com.zzq.mydemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.style.ReplacementSpan;

import androidx.annotation.IntRange;

/**
 * 通用於SpannableString的icon插入類
 */
public class CommonIconSpan extends ReplacementSpan {

    private Bitmap mBitmap;
    private int mWidth;
    private int mHeight;
    private int mBgMarginTop;
    private int mIntervalWidth;

    private RectF mTempF = new RectF();
    private Rect mTemp = new Rect();

    private CommonIconSpan() {
        super();
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        return mWidth + mIntervalWidth;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        mTempF.set(x, top + mBgMarginTop, x + mWidth, top + mBgMarginTop + mHeight);
        mTemp.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
        paint.setFilterBitmap(true);
        paint.setDither(true);
        canvas.drawBitmap(mBitmap, mTemp, mTempF, paint);
    }

    // ---------------------- Builder ----------------------

    public static class Builder {

        private CommonIconSpan instance;

        private Builder(CommonIconSpan iconSpan) {
            this.instance = iconSpan;
        }

        /**
         * @param context context
         * @param id      要展示的圖片資源id
         * @param height  要展示的圖片高度,根據高度計算寬度
         */
        public Builder(Context context, int id, @IntRange(from = 0) int height) {
            this(new CommonIconSpan());
            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), id);
            this.instance.mBitmap = bitmap;
            this.instance.mHeight = height;
            final int resourceWidth = bitmap.getWidth();
            final int resourceHeight = bitmap.getHeight();
            this.instance.mWidth = height * resourceWidth / resourceHeight;
        }

        /**
         * @param bitmap 要展示的圖片資源
         * @param height 要展示的圖片高度,根據高度計算寬度
         */
        public Builder(Bitmap bitmap, @IntRange(from = 0) int height) {
            this(new CommonIconSpan());
            this.instance.mBitmap = bitmap;
            this.instance.mHeight = height;
            final int resourceWidth = bitmap.getWidth();
            final int resourceHeight = bitmap.getHeight();
            this.instance.mWidth = height * resourceWidth / resourceHeight;
        }

        public Builder setIconBgMarginTop(@IntRange(from = 0) int bgMarginTop) {
            this.instance.mBgMarginTop = bgMarginTop;
            return this;
        }

        public Builder setIconIntervalWidth(@IntRange(from = 0) int intervalWidth) {
            this.instance.mIntervalWidth = intervalWidth;
            return this;
        }

        public CommonIconSpan build() {
            return this.instance;
        }
    }
}
package com.zzq.mydemo;

import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.text.style.ReplacementSpan;

import androidx.annotation.ColorInt;
import androidx.annotation.IntRange;

/**
 * 通用於SpannableString的帶背景文字插入類
 */
public class CommonTextSpan extends ReplacementSpan {

    private final int TYPE_FILL = 1;
    private final int TYPE_STROKE = 2;
    private final int TYPE_GRADIENT = 3;
    private int mType = TYPE_FILL;

    private int mHeight;
    private int mTextColor = 0xffffffff;
    private int mTextSize;
    private int mTextPadding;
    private int mBgRadius;
    private int mBgColor = 0xffffffff;
    private int mBgStrokeWidth;
    private int mStartColor = 0xffffffff;
    private int mEndColor = 0xffffffff;
    private int mBgMarginTop;
    private int mIntervalWidth;
    private RectF mTempF = new RectF();

    private CommonTextSpan() {
        super();
    }

    private void setFill(int bgColor) {
        this.mBgColor = bgColor;
        this.mType = TYPE_FILL;
    }

    private void setBgStroke(int bgColor, int strokeWidth) {
        this.mBgColor = bgColor;
        this.mBgStrokeWidth = strokeWidth;
        this.mType = TYPE_STROKE;
    }

    private void setGradient(int startColor, int endColor) {
        this.mStartColor = startColor;
        this.mEndColor = endColor;
        this.mType = TYPE_GRADIENT;
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        float oldTextSize = paint.getTextSize();
        paint.setTextSize(mTextSize);
        int size = ((int) paint.measureText(text, start, end)) + mTextPadding * 2 + mIntervalWidth;
        paint.setTextSize(oldTextSize);
        return size;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        int color1 = paint.getColor();

        switch (mType) {
            case TYPE_GRADIENT:
                LinearGradient linearGradient = new LinearGradient(
                        x, top, y, bottom,
                        mStartColor, mEndColor,
                        Shader.TileMode.CLAMP);
                paint.setShader(linearGradient);
                paint.setStyle(Paint.Style.FILL);
                break;
            case TYPE_STROKE:
                paint.setColor(mBgColor);
                paint.setStyle(Paint.Style.STROKE);
                paint.setStrokeWidth(mBgStrokeWidth);
                break;
            default:
                paint.setColor(mBgColor);
                paint.setStyle(Paint.Style.FILL);
        }

        paint.setTextSize(mTextSize);
        mTempF.set(x, top + mBgMarginTop, x + ((int) paint.measureText(text, start, end)) + mTextPadding * 2, top + mBgMarginTop + mHeight);
        canvas.drawRoundRect(mTempF, mBgRadius, mBgRadius, paint);

        paint.setShader(null);
        paint.setColor(this.mTextColor);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setStrokeWidth(1);
        float textY = top + mBgMarginTop + mHeight / 2 - (paint.descent() + paint.ascent()) / 2;
        canvas.drawText(text, start, end, x + mTextPadding, textY, paint);
        paint.setColor(color1);
    }

    // ---------------------- Builder ----------------------

    public static class Builder {

        private Builder(CommonTextSpan textSpan) {
            this.instance = textSpan;
        }

        private CommonTextSpan instance;

        public Builder(@IntRange(from = 0) int height) {
            this(new CommonTextSpan());
            this.instance.mHeight = height;
        }

        public Builder setTextColor(@ColorInt int textColor) {
            this.instance.mTextColor = textColor;
            return this;
        }

        public Builder setTextSize(@IntRange(from = 0) int textSize) {
            this.instance.mTextSize = textSize;
            return this;
        }

        public Builder setTextPadding(@IntRange(from = 0) int textPadding) {
            this.instance.mTextPadding = textPadding;
            return this;
        }

        public Builder setBgMarginTop(@IntRange(from = 0) int bgMarginTop) {
            this.instance.mBgMarginTop = bgMarginTop;
            return this;
        }

        public Builder setBgRadius(@IntRange(from = 0) int bgRadius) {
            this.instance.mBgRadius = bgRadius;
            return this;
        }

        public Builder setIntervalWidth(@IntRange(from = 0) int intervalWidth) {
            this.instance.mIntervalWidth = intervalWidth;
            return this;
        }

        public Builder setBgFill(@ColorInt int bgColor) {
            this.instance.setFill(bgColor);
            return this;
        }

        public Builder setBgStroke(@ColorInt int bgColor, @IntRange(from = 0) int strokeWidth) {
            this.instance.setBgStroke(bgColor, strokeWidth);
            return this;
        }

        public Builder setBgGradient(@ColorInt int startColor, @ColorInt int endColor) {
            this.instance.setGradient(startColor, endColor);
            return this;
        }

        public CommonTextSpan build() {
            return this.instance;
        }
    }
}

參考文章:
1、https://www.jianshu.com/p/94687b604061
2、https://blog.csdn.net/liujun03/article/details/82789704
3、https://www.cnblogs.com/Alex–Yang/p/3386863.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章