Under the Android.text. style package, there are some Span classes that allow us to accomplish special things in a TextView. (For example, some contents are different in color, font, size, etc., and some fonts can be clicked.)

There’s also a SpannableStringBuilder that helps us set spans.

SpannableStringBuilder

SpannableStringBuilder makes it easier for us to better set up the corresponding Span.

Set Span: SpannableStringBuilder. SetSpan (Object what, int start, int the end, int flags)

The Flag here indicates whether start and end are open or closed intervals.

Flag values:

Spanned. SPAN_EXCLUSIVE_EXCLUSIVE — — (a, b)

Spanned. SPAN_EXCLUSIVE_INCLUSIVE — — (a, b]

Spanned. SPAN_INCLUSIVE_EXCLUSIVE — — [a, b)

Spanned. SPAN_INCLUSIVE_INCLUSIVE — — [a, b]

URLSpan

Function: Click text to open a URL.

SpannableStringBuilder ssb =new SpannableStringBuilder(content); ssb.setSpan(new URLSpan("https://github.com/CaMnter"), start, sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); tv.setText(ssb); / / when you click on the link that has to perform an action, must be set MovementMethod TV. The object setMovementMethod (LinkMovementMethod. GetInstance ()); // Set the color after the click, this involves the ClickableSpan click background TV.sethighlightColor (0xff8FABCC);Copy the code

Display effect:

UnderlineSpan

Function: Set text underline.


SpannableStringBuilder ssb =new SpannableStringBuilder(content);
ssb.setSpan(newUnderlineSpan(), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);

Copy the code

Display effect:

TypefaceSpan

Function: Set text font.

Comments in the source of the TypefaceSpan(String family) constructor indicate three system fonts:

  • monospace
  • serif
  • sans-serif

/**

    *@param family The font family for this typeface.  Examples include

    * "monospace"."serif".and "sans-serif".

    */

public TypefaceSpan(String family){
        mFamily = family;
    }

SpannableStringBuilder ssb =new SpannableStringBuilder(content);
ssb.setSpan(newTypefaceSpan("serif"), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);

Copy the code

Display effect:

TextAppearanceSpan

Function: set text font, text style (bold, italic, etc.), text color state, text underline color state, etc.

Three constructors of TextAppearanceSpan


TextAppearanceSpan(Context context, int appearance)

TextAppearanceSpan(Context context, int appearance, int colorList)

TextAppearanceSpan(String family, int style, int size,ColorStateList color, ColorStateList linkColor)

Copy the code

Family values:

  • monospace
  • serif
  • sans-serif

Style values:

  • Typeface.NORMAL
  • Typeface.BOLD
  • Typeface.ITALIC
  • Typeface.BOLD_ITALIC

Size: indicates the font size (unit: px)

SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ColorStateList colorStateList = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    colorStateList = this.activity.getColorStateList(R.color.selector_apperarance_span);
} else {
    try {
        colorStateList = ColorStateList.createFromXml(this.activity.getResources(), this.activity.getResources().getXml(R.color.selector_apperarance_span));
    } catch (XmlPullParserException | IOException e) {
        e.printStackTrace();
    }
}
ssb.setSpan(newTextAppearanceSpan("serif", Typeface.BOLD_ITALIC, this.activity.getResources().getDimensionPixelSize(R.dimen.text_appearance_span), colorStateList, colorStateList), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);
Copy the code

Display effect:

TabStopSpan.Standard

Function: Offset of each line’s MarginLeft (related to \t and \n).

TabStopSpan.Standard(int where)

SpannableStringBuilder ssb = new SpannableStringBuilder(content); String[] subs = content.split(" "); ssb = new SpannableStringBuilder(); Standard related to \t and \n * TabStopSpan.Standard related to \t and \n */ for (String sub1: subs) { ssb.append("\t").append(sub1).append(" "); ssb.append("\n"); } ssb.setSpan(new TabStopSpan.Standard(126), 0, ssb.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); tv.setText(ssb);Copy the code

Display effect:####SuperscriptSpan function: text set as superscript, used in mathematical formulas.

SuperscriptSpan(Parcel src)

SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.replace(start, start + sub.length(), "Save6");
Parcel parcel = Parcel.obtain();
parcel.writeInt(6);
int sixPosition = ssb.toString().indexOf("6");
ssb.setSpan(new SuperscriptSpan(parcel), sixPosition, sixPosition + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
parcel.recycle();
tv.setText(ssb);
Copy the code

Display effect:

SubscriptSpan

Function: text set as subscript, used in chemical formula. SubscriptSpan(Parcel src)

SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.replace(start, start + sub.length(), "Save6");
Parcel parcel = Parcel.obtain();
parcel.writeInt(6);
int sixPosition = ssb.toString().indexOf("6");
ssb.setSpan(new SubscriptSpan(parcel), sixPosition, sixPosition + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
parcel.recycle();
tv.setText(ssb);

Copy the code

Display effect:####StrikethroughSpan function: Text set delete line.

StrikethroughSpan()


SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.setSpan(new StrikethroughSpan(), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);

Copy the code

Display effect:

ScaleXSpan

Function: Text horizontal zoom.

ScaleXSpan(float proportion)

Proportion: Zoom ratio

SpannableStringBuilder ssb = new SpannableStringBuilder(content); Ssb.setspan (new ScaleXSpan(2.0f), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); tv.setText(ssb);Copy the code

Display effect:

StyleSpan

Function: Text setting style (normal, bold, italic, bold italic).

StyleSpan(int style)

Style values:

  • Typeface.NORMAL
  • Typeface.BOLD
  • Typeface.ITALIC
  • Typeface.BOLD_ITALIC
SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);

Copy the code

Display effect:

RelativeSizeSpan

Function: Set the relative size of the text, which refers to the relative size of the text.

RelativeSizeSpan(float proportion)

Proportion: size proportion

SpannableStringBuilder ssb = new SpannableStringBuilder(content); Ssb.setspan (New RelativeSizeSpan(6.0f), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); tv.setText(ssb);Copy the code

Display effect:

QuoteSpan

Function: Set text to the left side of the display reference style (a vertical line).

QuoteSpan(@ColorInt int color)

Color: the color of the vertical line


SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.setSpan(new QuoteSpan(0xff000000), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);
Copy the code

Display effect:

MaskFilterSpan

Function: Set text blur effect and emblazon effect.

MaskFilterSpan(MaskFilter filter)

MaskFilter values:

  • BlurMaskFilter: Blurring effect
  • EmbossMaskFilter: embossed effect
SpannableStringBuilder ssb = new SpannableStringBuilder(content); MaskFilterSpan embossMaskFilterSpan = New MaskFilterSpan(New float[]{3, 3, 9}, 3.0F, 12, 16); ssb.setSpan(embossMaskFilterSpan, start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); contentTV.setText(ssb); String you = "you"; int indexYou = content.indexOf(you); MaskFilterSpan blurMaskFilterSpan = new MaskFilterSpan(new BlurMaskFilter(3, BlurMaskFilter.Blur.OUTER)); ssb.setSpan(blurMaskFilterSpan, indexYou, indexYou + you.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); tv.setText(ssb);Copy the code

Display effect:

LeadingMarginSpan.Standard

Function: Set text indentation.

LeadingMarginSpan.Standard(int first, int rest)

First: margin left offset of the first line.

Rest: Margin left offset of other rows.

SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.append(" ")
        .append(ssb.toString())
        .append(ssb.toString())
        .append(ssb.toString());
ssb.setSpan(new LeadingMarginSpan.Standard(96, 36), 0, ssb.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);
Copy the code

Display effect:

ImageSpan

Function: Text insert picture.

There are many ways to construct it:

ImageSpan(Context context, Bitmap b)

ImageSpan(Context context, Bitmap b, int verticalAlignment)

ImageSpan(Drawable d)

ImageSpan(Drawable d, int verticalAlignment)

ImageSpan(Drawable d, String source)

ImageSpan(Drawable d, String source, int verticalAlignment)

ImageSpan(Context context, Uri uri)

ImageSpan(Context context, Uri uri, int verticalAlignment)

ImageSpan(Context context, @DrawableRes int resourceId)

ImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment)

VerticalAlignment values:

  • ImageSpan.ALIGN_BOTTOM
  • ImageSpan.ALIGN_BASELINE

Source: The native path of the image String. / / XXX (XXX XXX. JPG)

Uri: The native URI of the image.

SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.replace(start, start + sub.length(), " Save");
ssb.setSpan(new ImageSpan(this.activity, R.mipmap.ic_mm_1, ImageSpan.ALIGN_BASELINE), 0, 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);
Copy the code

Display effect:####IconMarginSpan function: text insert image +Margin.

IconMarginSpan(Bitmap b, int pad)

Pad: indicates the margin offset.

SpannableStringBuilder ssb = new SpannableStringBuilder(content);
Bitmap bitmap = BitmapFactory.decodeResource(this.activity.getResources(), R.mipmap.ic_mm_1);
ssb.setSpan(new IconMarginSpan(bitmap, 60), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//bitmap.recycle();
tv.setText(ssb);
Copy the code

Display effect:

ForegroundColorSpan

Function: Set text color.

ForegroundColorSpan(@ColorInt int color)

SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.setSpan(new ForegroundColorSpan(0xff303F9F), start, start + sub.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
tv.setText(ssb);
Copy the code

Display effect:

DrawableMarginSpan

Function: Text insert image +Margin.

DrawableMarginSpan(Drawable b, int pad)

Pad: indicates the margin offset.

SpannableStringBuilder ssb = new SpannableStringBuilder(content);
ssb.setSpan(new DrawableMarginSpan(ResourcesUtil.getDrawable(this.activity, R.mipmap.ic_mm_1), 6), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(ssb);
Copy the code

Display effect:

BulletSpan

Function: similar to HTML

  • Dot effect of label.

    BulletSpan(int gapWidth, int color)

    GapWidth: Distance between dots and text.

    Color: dot color.

    SpannableStringBuilder ssb = new SpannableStringBuilder(content);
    ssb.setSpan(new BulletSpan(66, 0xff303F9F), start, start + sub.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    tv.setText(ssb);
    Copy the code

    Display effect:

    BackgroundColorSpan

    Function: Set background color.

    BackgroundColorSpan(int color)

    SpannableStringBuilder ssb = new SpannableStringBuilder(content);
    String you = "you";
    int indexYou = content.indexOf(you);
    ssb.setSpan(new BackgroundColorSpan(0x2f303F9F), start, start + sub.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    ssb.setSpan(new BackgroundColorSpan(0x2fFF4081), indexYou, indexYou + you.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    tv.setText(ssb);
    Copy the code

    Display effect:

    AlignmentSpan.Standard

    Function: Set text alignment.

    AlignmentSpan.Standard(Layout.Alignment align)

    Align the values:

    • Layout.Alignment.ALIGN_NORMAL
    • Layout.Alignment.ALIGN_OPPOSITE
    • Layout.Alignment.ALIGN_CENTER
    
    SpannableStringBuilder ssb = new SpannableStringBuilder(content);
    ssb.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    tv.setText(ssb);
    Copy the code

    Display effect:

    AbsoluteSizeSpan

    Function: Set absolute text size.

    AbsoluteSizeSpan(int size, boolean dip)

    Size: The default unit is px. Dip: true is the size unit of dip, false is px.

    SpannableStringBuilder ssb = new SpannableStringBuilder(content);
    ssb.setSpan(new AbsoluteSizeSpan(26, true), start, start + sub.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    tv.setText(ssb);
    Copy the code

    Display effect:

    ClickableSpan

    Function: text can be clicked.

    Abstract classes that need to extend their own implementations.

    ClickableSpanNoUnderline

    public class ClickableSpanNoUnderline extends ClickableSpan { private static final String TAG = "ClickableSpan"; private static final int NO_COLOR = -206; private int color; private OnClickListener onClickListener; public ClickableSpanNoUnderline(int color, OnClickListener onClickListener) { super(); this.color = color; this.onClickListener = onClickListener; } public ClickableSpanNoUnderline(OnClickListener onClickListener) { this(NO_COLOR, onClickListener); } /** * Makes the text underlined and in the link color. * * @param ds */ @Override public void updateDrawState(@NonNull  TextPaint ds) { super.updateDrawState(ds); // Set the text color if (this.color == NO_COLOR) {ds.setcolor (ds.linkcolor); } else { ds.setColor(this.color); } ds.clearShadowLayer(); // Remove the underline ds.setUnderlinetext (false); ds.bgColor = Color.TRANSPARENT; } /** * Performs the click action associated with this span. * * @param widget widget */ @Override public void onClick(View widget) { if (this.onClickListener ! = null) { this.onClickListener.onClick(widget, this); } else { Log.w(TAG, "listener was null"); }} /** * callback interface, Public interface OnClickListener<T extends ClickableSpanNoUnderline> {/** * ClickableSpan is clicked * * @param widget widget * @param span span */ void onClick(View widget, T span); }}Copy the code

    SpanClickableSpan

    public class SpanClickableSpan extends ClickableSpanNoUnderline { private String urlString; public String getUrlString() { return urlString; } public void setUrlString(String urlString) { this.urlString = urlString; } public SpanClickableSpan(int color, OnClickListener onClickListener) { super(color, onClickListener); } public SpanClickableSpan(OnClickListener onClickListener) { super(onClickListener); }}Copy the code

    Begin to use

    SpannableStringBuilder ssb = new SpannableStringBuilder(content); SpanClickableSpan spanClickableSpan = new SpanClickableSpan(0xffFF4081, New ClickableSpanNoUnderline. An OnClickListener < SpanClickableSpan > () {/ ClickableSpan by click on the * * * * * @ param widget widget * @param span span */ @Override public void onClick(View widget, SpanClickableSpan span) { String urlString = span.getUrlString(); if (TextUtils.isEmpty(urlString)) return; Uri uri = Uri.parse(urlString); Context context = widget.getContext(); Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); try { context.startActivity(intent); } catch (ActivityNotFoundException e) { Log.w("URLSpan", "Activity was not found for intent, " + intent.toString()); }}}); spanClickableSpan.setUrlString("https://github.com/CaMnter"); ssb.setSpan(spanClickableSpan, start, start + sub.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); tv.setText(ssb); / / when you click on the link that has to perform an action, must be set MovementMethod TV. The object setMovementMethod (LinkMovementMethod. GetInstance ()); // Set the color after the click, this involves the ClickableSpan click background TV.sethighlightColor (0x00000000);Copy the code

    Display effect:

    You may need this rich text editing class code:

    import android.graphics.Bitmap;
    import android.graphics.BlurMaskFilter;
    import android.graphics.Color;
    import android.graphics.Typeface;
    import android.graphics.drawable.Drawable;
    import android.net.Uri;
    import android.text.Layout.Alignment;
    import android.text.SpannableStringBuilder;
    import android.text.Spanned;
    import android.text.TextPaint;
    import android.text.style.AlignmentSpan;
    import android.text.style.BackgroundColorSpan;
    import android.text.style.BulletSpan;
    import android.text.style.ClickableSpan;
    import android.text.style.ForegroundColorSpan;
    import android.text.style.ImageSpan;
    import android.text.style.LeadingMarginSpan;
    import android.text.style.MaskFilterSpan;
    import android.text.style.QuoteSpan;
    import android.text.style.RelativeSizeSpan;
    import android.text.style.ScaleXSpan;
    import android.text.style.StrikethroughSpan;
    import android.text.style.StyleSpan;
    import android.text.style.SubscriptSpan;
    import android.text.style.SuperscriptSpan;
    import android.text.style.TypefaceSpan;
    import android.text.style.URLSpan;
    import android.text.style.UnderlineSpan;
    import android.util.Log;
    import android.view.View;
    import android.widget.TextView;
    
    import androidx.annotation.ColorInt;
    import androidx.annotation.DrawableRes;
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    
    import com.example.mylibrary.AUtil;
    
    import static android.graphics.BlurMaskFilter.Blur;
    
    /**
     * 富文本编辑类
     */
    public class SpanStringUtils {
    
        /*-  样例
        private void initText() {
            ClickableSpan clickableSpan = new ClickableSpan() {
                @Override
                public void onClick(View widget) {
                    RxToast.showToast(mContext, "事件触发了", 500);
                }
    
                @Override
                public void updateDrawState(TextPaint ds) {
                    ds.setColor(Color.BLUE);
                    ds.setUnderlineText(false);
                }
            };
    
    
            // 响应点击事件的话必须设置以下属性
            tv.setMovementMethod(LinkMovementMethod.getInstance());
    
            SpanStringUtils.getBuilder("").setBold().setAlign(Layout.Alignment.ALIGN_CENTER)
                    .append("测试").append("Url\n").setUrl(URL_VONTOOLS)
                    .append("列表项\n").setBullet(60, getResources().getColor(R.color.baby_blue))
                    .append("  测试引用\n").setQuoteColor(getResources().getColor(R.color.baby_blue))
                    .append("首行缩进\n").setLeadingMargin(30, 50)
                    .append("测试").append("上标").setSuperscript().append("下标\n").setSubscript()
                    .append("测试").append("点击事件\n").setClickSpan(clickableSpan)
                    .append("测试").append("serif 字体\n").setFontFamily("serif")
                    .append("测试").append("图片").append("图片\n").setResourceId(R.drawable.logo)
                    .append("测试").append("前景色").setForegroundColor(Color.GREEN).append("背景色\n").setBackgroundColor(getResources().getColor(R.color.baby_blue))
                    .append("测试").append("删除线").setStrikeThrough().append("下划线\n").setUnderline()
                    .append("测试").append("sans-serif 字体\n").setFontFamily("sans-serif")
                    .append("测试").append("2倍字体\n").setProportion(2)
                    .append("测试").append("monospace字体\n").setFontFamily("monospace")
                    .append("测试").append("普通模糊效果字体\n").setBlur(3, BlurMaskFilter.Blur.NORMAL)
                    .append("测试").append(" 粗体 ").setBold().append(" 斜体 ").setItalic().append(" 粗斜体 \n").setBoldItalic()
                    .append("测试").append("横向2倍字体\n").setXProportion(2)
    
                    .append("\n测试正常对齐\n").setAlign(Layout.Alignment.ALIGN_NORMAL)
                    .append("测试居中对齐\n").setAlign(Layout.Alignment.ALIGN_CENTER)
                    .append("测试相反对齐\n").setAlign(Layout.Alignment.ALIGN_OPPOSITE)
                    .into(tv);
        }
        //单独设置图片
        SpanStringUtils.getBuilder("图片").setDrawable(tv, getDrawable(R.drawable.ic_stat_name), SpanStringUtils.DrawableType.RIGHT);
    
        private SpannableStringUtils() {
            throw new UnsupportedOperationException("u can't instantiate me...");
        }*/
    
        /**
         * 获取建造者
         *
         * @param text 样式字符串文本
         * @return {@link Builder}
         */
        public static Builder getBuilder(@NonNull CharSequence text) {
            return new Builder(text);
        }
    
        //
        public static class Builder {
            //
            private int defaultValue = 0x12000000;
            private CharSequence text;
    
            private int flag;
            @ColorInt
            private int foregroundColor;
            @ColorInt
            private int backgroundColor;
            @ColorInt
            private int quoteColor;
    
    
            private boolean isLeadingMargin;
            private int first;
            private int rest;
    
            private boolean isBullet;
            private int gapWidth;
            private int bulletColor;
    
            private float proportion;
            private float xProportion;
            private boolean isStrikeThrough;
            private boolean isUnderline;
            private boolean isSuperscript;
            private boolean isSubscript;
            private boolean isBold;
            private boolean isItalic;
            private boolean isBoldItalic;
            private String fontFamily;
            private Alignment align;
    
            private boolean imageIsBitmap;
            private Bitmap bitmap;
            private boolean imageIsDrawable;
            private Drawable drawable;
            private boolean imageIsUri;
            private Uri uri;
            private boolean imageIsResourceId;
            @DrawableRes
            private int resourceId;
    
            private ClickableSpan clickSpan;
            private String url;
    
            private boolean isBlur;
            private float radius;
            private Blur style;
    
            private SpannableStringBuilder mBuilder;
    
            private Builder(@NonNull CharSequence text) {
                this.text = text;
                flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
                foregroundColor = defaultValue;
                backgroundColor = defaultValue;
                quoteColor = defaultValue;
                proportion = -1;
                xProportion = -1;
                mBuilder = new SpannableStringBuilder();
            }
    
            /**
             * 设置标识
             *
             * @param flag <ul>
             *             <li>{@link Spanned#SPAN_INCLUSIVE_EXCLUSIVE}</li>
             *             <li>{@link Spanned#SPAN_INCLUSIVE_INCLUSIVE}</li>
             *             <li>{@link Spanned#SPAN_EXCLUSIVE_EXCLUSIVE}</li>
             *             <li>{@link Spanned#SPAN_EXCLUSIVE_INCLUSIVE}</li>
             *             </ul>
             * @return {@link Builder}
             */
            public Builder setFlag(int flag) {
                this.flag = flag;
                return this;
            }
    
    
            /**
             * 设置前景色
             *
             * @param color 前景色
             * @return {@link Builder}
             */
            public Builder setForegroundColor(@ColorInt int color) {
                this.foregroundColor = color;
                return this;
            }
    
            /**
             * 设置背景色
             *
             * @param color 背景色
             * @return {@link Builder}
             */
            public Builder setBackgroundColor(@ColorInt int color) {
                this.backgroundColor = color;
                return this;
            }
    
            /**
             * 设置引用线的颜色
             *
             * @param color 引用线的颜色
             * @return {@link Builder}
             */
            public Builder setQuoteColor(@ColorInt int color) {
                this.quoteColor = color;
                return this;
            }
    
            /**
             * 设置缩进
             *
             * @param first 首行缩进
             * @param rest  剩余行缩进
             * @return {@link Builder}
             */
            public Builder setLeadingMargin(int first, int rest) {
                this.first = first;
                this.rest = rest;
                isLeadingMargin = true;
                return this;
            }
    
            /**
             * 设置列表标记
             *
             * @param gapWidth 列表标记和文字间距离
             * @param color    列表标记的颜色
             * @return {@link Builder}
             */
            public Builder setBullet(int gapWidth, int color) {
                this.gapWidth = gapWidth;
                bulletColor = color;
                isBullet = true;
                return this;
            }
    
            /**
             * 设置字体比例
             *
             * @param proportion 比例
             * @return {@link Builder}
             */
            public Builder setProportion(float proportion) {
                this.proportion = proportion;
                return this;
            }
    
            /**
             * 设置字体横向比例
             *
             * @param proportion 比例
             * @return {@link Builder}
             */
            public Builder setXProportion(float proportion) {
                this.xProportion = proportion;
                return this;
            }
    
            /**
             * 设置删除线
             *
             * @return {@link Builder}
             */
            public Builder setStrikeThrough() {
                this.isStrikeThrough = true;
                return this;
            }
    
            /**
             * 设置下划线
             *
             * @return {@link Builder}
             */
            public Builder setUnderline() {
                this.isUnderline = true;
                return this;
            }
    
            /**
             * 设置上标
             *
             * @return {@link Builder}
             */
            public Builder setSuperscript() {
                this.isSuperscript = true;
                return this;
            }
    
            /**
             * 设置下标
             *
             * @return {@link Builder}
             */
            public Builder setSubscript() {
                this.isSubscript = true;
                return this;
            }
    
            /**
             * 设置粗体
             *
             * @return {@link Builder}
             */
            public Builder setBold() {
                isBold = true;
                return this;
            }
    
            /**
             * 设置斜体
             *
             * @return {@link Builder}
             */
            public Builder setItalic() {
                isItalic = true;
                return this;
            }
    
            /**
             * 设置粗斜体
             *
             * @return {@link Builder}
             */
            public Builder setBoldItalic() {
                isBoldItalic = true;
                return this;
            }
    
            /**
             * 设置字体
             *
             * @param fontFamily 字体
             *                   <ul>
             *                   <li>monospace</li>
             *                   <li>serif</li>
             *                   <li>sans-serif</li>
             *                   </ul>
             * @return {@link Builder}
             */
            public Builder setFontFamily(@Nullable String fontFamily) {
                this.fontFamily = fontFamily;
                return this;
            }
    
            /**
             * 设置对齐
             *
             * @param align 对其方式
             *              <ul>
             *              <li>{@link Alignment#ALIGN_NORMAL}正常</li>
             *              <li>{@link Alignment#ALIGN_OPPOSITE}相反</li>
             *              <li>{@link Alignment#ALIGN_CENTER}居中</li>
             *              </ul>
             * @return {@link Builder}
             */
            public Builder setAlign(@Nullable Alignment align) {
                this.align = align;
                return this;
            }
    
            /**
             * 设置图片
             *
             * @param bitmap 图片位图
             * @return {@link Builder}
             */
            public Builder setBitmap(@NonNull Bitmap bitmap) {
                this.bitmap = bitmap;
                imageIsBitmap = true;
                return this;
            }
    
            /**
             * 设置图片
             *
             * @param drawable 图片资源
             * @return {@link Builder}
             */
            public Builder setDrawable(@NonNull Drawable drawable) {
                this.drawable = drawable;
                imageIsDrawable = true;
                return this;
            }
    
            /**
             * 设置图片
             *
             * @param uri 图片uri
             * @return {@link Builder}
             */
            public Builder setUri(@NonNull Uri uri) {
                this.uri = uri;
                imageIsUri = true;
                return this;
            }
    
            /**
             * 设置图片
             *
             * @param resourceId 图片资源id
             * @return {@link Builder}
             */
            public Builder setResourceId(@DrawableRes int resourceId) {
                this.resourceId = resourceId;
                imageIsResourceId = true;
                return this;
            }
    
            /**
             * 设置点击事件
             * <p>需添加view.setMovementMethod(LinkMovementMethod.getInstance())</p>
             *
             * @param clickSpan 点击事件
             * @return {@link Builder}
             */
            public Builder setClickSpan(@NonNull ClickableSpan clickSpan) {
                this.clickSpan = clickSpan;
                return this;
            }
    
            /**
             * 设置超链接
             * <p>需添加view.setMovementMethod(LinkMovementMethod.getInstance())</p>
             *
             * @param url 超链接
             * @return {@link Builder}
             */
            public Builder setUrl(@NonNull String url) {
                this.url = url;
                return this;
            }
    
            /**
             * 设置模糊
             * <p>尚存bug,其他地方存在相同的字体的话,相同字体出现在之前的话那么就不会模糊,出现在之后的话那会一起模糊</p>
             * <p>推荐还是把所有字体都模糊这样使用</p>
             *
             * @param radius 模糊半径(需大于0)
             * @param style  模糊样式<ul>
             *               <li>{@link Blur#NORMAL}</li>
             *               <li>{@link Blur#SOLID}</li>
             *               <li>{@link Blur#OUTER}</li>
             *               <li>{@link Blur#INNER}</li>
             *               </ul>
             * @return {@link Builder}
             */
            public Builder setBlur(float radius, Blur style) {
                this.radius = radius;
                this.style = style;
                this.isBlur = true;
                return this;
            }
    
            /**
             * 追加样式字符串
             *
             * @param text 样式字符串文本
             * @return {@link Builder}
             */
            public Builder append(@NonNull CharSequence text) {
                setSpan();
                this.text = text;
                return this;
            }
    
            /**
             * 创建样式字符串
             *
             * @return 样式字符串
             */
            public SpannableStringBuilder create() {
                setSpan();
                return mBuilder;
            }
    
            public void into(TextView textView) {
                setSpan();
                if (textView != null) {
                    textView.setText(mBuilder);
                }
            }
    
            /**
             * 设置样式
             */
            private void setSpan() {
                int start = mBuilder.length();
                Log.i("SpanStringUtils", "start = " + start);
                mBuilder.append(this.text);
                int end = mBuilder.length();
                Log.i("SpanStringUtils", "end = " + end);
                if (foregroundColor != defaultValue) {
                    mBuilder.setSpan(new ForegroundColorSpan(foregroundColor), start, end, flag);
                    foregroundColor = defaultValue;
                }
                if (backgroundColor != defaultValue) {
                    mBuilder.setSpan(new BackgroundColorSpan(backgroundColor), start, end, flag);
                    backgroundColor = defaultValue;
                }
                if (isLeadingMargin) {
                    mBuilder.setSpan(new LeadingMarginSpan.Standard(first, rest), start, end, flag);
                    isLeadingMargin = false;
                }
                if (quoteColor != defaultValue) {
                    mBuilder.setSpan(new QuoteSpan(quoteColor), start, end, 0);
                    quoteColor = defaultValue;
                }
                if (isBullet) {
                    mBuilder.setSpan(new BulletSpan(gapWidth, bulletColor), start, end, 0);
                    isBullet = false;
                }
                if (proportion != -1) {
                    mBuilder.setSpan(new RelativeSizeSpan(proportion), start, end, flag);
                    proportion = -1;
                }
                if (xProportion != -1) {
                    mBuilder.setSpan(new ScaleXSpan(xProportion), start, end, flag);
                    xProportion = -1;
                }
                if (isStrikeThrough) {
                    mBuilder.setSpan(new StrikethroughSpan(), start, end, flag);
                    isStrikeThrough = false;
                }
                if (isUnderline) {
                    mBuilder.setSpan(new UnderlineSpan(), start, end, flag);
                    isUnderline = false;
                }
                if (isSuperscript) {
                    mBuilder.setSpan(new SuperscriptSpan(), start, end, flag);
                    isSuperscript = false;
                }
                if (isSubscript) {
                    mBuilder.setSpan(new SubscriptSpan(), start, end, flag);
                    isSubscript = false;
                }
                if (isBold) {
                    mBuilder.setSpan(new StyleSpan(Typeface.BOLD), start, end, flag);
                    isBold = false;
                }
                if (isItalic) {
                    mBuilder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flag);
                    isItalic = false;
                }
                if (isBoldItalic) {
                    mBuilder.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, flag);
                    isBoldItalic = false;
                }
                if (fontFamily != null) {
                    mBuilder.setSpan(new TypefaceSpan(fontFamily), start, end, flag);
                    fontFamily = null;
                }
                if (align != null) {
                    mBuilder.setSpan(new AlignmentSpan.Standard(align), start, end, flag);
                    align = null;
                }
                if (imageIsBitmap || imageIsDrawable || imageIsUri || imageIsResourceId) {
                    if (imageIsBitmap) {
                        mBuilder.setSpan(new ImageSpan(AUtil.getInstance().getContext(), bitmap), start, end, flag);
                        bitmap = null;
                        imageIsBitmap = false;
                    } else if (imageIsDrawable) {
    //                    drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());// 设置图片宽高
                        drawable.setBounds(0, 0, 50, 50);
                        mBuilder.setSpan(new ImageSpan(drawable), start, end, flag);
                        drawable = null;
                        imageIsDrawable = false;
                    } else if (imageIsUri) {
                        mBuilder.setSpan(new ImageSpan(AUtil.getInstance().getContext(), uri), start, end, flag);
                        uri = null;
                        imageIsUri = false;
                    } else {
                        mBuilder.setSpan(new ImageSpan(AUtil.getInstance().getContext(), resourceId), start, end, flag);
                        resourceId = 0;
                        imageIsResourceId = false;
                    }
                }
                if (clickSpan != null) {
                    mBuilder.setSpan(clickSpan, start, end, flag);
                    clickSpan = null;
                }
                if (url != null) {
                    mBuilder.setSpan(new URLSpan(url), start, end, flag);
                    url = null;
                }
                if (isBlur) {
                    mBuilder.setSpan(new MaskFilterSpan(new BlurMaskFilter(radius, style)), start, end, flag);
                    isBlur = false;
                }
                flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
            }
    
            /**
             * 给textview设置drawable
             *
             * @param textView 显示控件
             * @param drawable 图片
             * @param type     DrawableType.LEFT 显示在文字左面
             *                 DrawableType.RIGHT 显示在文字右面
             *                 DrawableType.TOP 显示在文字上面
             *                 DrawableType.BOTTOM 显示在文字下面
             *                 tv.setText("左侧图片");
             *                 SpanStringUtils.getBuilder("图片").setDrawable(tv1, getDrawable(R.drawable.ic_stat_name), 1);
             */
            public void setDrawable(TextView textView, Drawable drawable, @DrawableType int type) {
                drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());// 设置图片宽高
                switch (type) {
                    case DrawableType.LEFT:
                        textView.setCompoundDrawables(drawable, null, null, null);// 设置到控件中
                        break;
                    case DrawableType.TOP:
                        textView.setCompoundDrawables(null, drawable, null, null);// 设置到控件中
                        break;
                    case DrawableType.RIGHT:
                        textView.setCompoundDrawables(null, null, drawable, null);// 设置到控件中
                        break;
                    case DrawableType.BOTTOM:
                        textView.setCompoundDrawables(null, null, null, drawable);// 设置到控件中
                        break;
    
                }
    
            }
    
            public ClickableSpan getClickableSpan(final String textColor) {
                //文字跨度
                ClickableSpan clickableSpan = new ClickableSpan() {
                    @Override
                    public void onClick(View widget) {
                    }
    
                    @Override
                    public void updateDrawState(TextPaint ds) {
                        ds.setColor(Color.parseColor(textColor));
                        ds.setUnderlineText(false);
                    }
                };
    
                return clickableSpan;
            }
    
        }
    
        public @interface DrawableType {
            int LEFT = 1;
            int TOP = 2;
            int RIGHT = 3;
            int BOTTOM = 4;
        }
    
    }
    
    Copy the code