一、TvUtils 工具类,主要帮助实现富文本相关功能
public class TvUtils {/*** 富文本设置字体大小** @param srcString* @param tartgetString* @param txtSize* @return*/public static SpannableString setTextPartTextSize(String srcString, String tartgetString, int txtSize) {int position = srcString.indexOf(tartgetString);SpannableString mSp = new SpannableString(srcString);if (position >= 0) {mSp.setSpan(new AbsoluteSizeSpan(txtSize, false), position, position + tartgetString.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}return mSp;}/**富文本设置字体颜色和大小* @param srcString* @param tartgetString* @param targetColor* @param txtSizeDp* @return*/public static SpannableString setTextPartTextSize(String srcString, String tartgetString, int targetColor, int txtSizeDp) {int position = srcString.indexOf(tartgetString);SpannableString mSp = new SpannableString(srcString);if (position >= 0) {mSp.setSpan(new AbsoluteSizeSpan(txtSizeDp, true), position, position + tartgetString.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);mSp.setSpan(new ForegroundColorSpan(targetColor), position, position + tartgetString.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);}return mSp;}/**富文本设置部分文字颜色* @param srcString* @param tartgetString* @param targetColor* @return*/public static SpannableString setTextPartColor(String srcString,String tartgetString,int targetColor){int position = srcString.indexOf(tartgetString);SpannableString mSp = new SpannableString(srcString);if (position >= 0) {mSp.setSpan(new ForegroundColorSpan(targetColor), position, position + tartgetString.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);}return mSp;}/**富文本设置部分文字背景色* @param srcString* @param tartgetString* @param targetColor* @return*/public static SpannableString setTextPartBg(String srcString,String tartgetString,int targetColor){int position = srcString.indexOf(tartgetString);SpannableString mSp = new SpannableString(srcString);if (position >= 0) {mSp.setSpan(new BackgroundColorSpan(targetColor), position, position + tartgetString.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);}return mSp;}/**设置中划线* @param src* @param start* @param end* @return*/public static SpannableString setStrikethrough(String src,int start,int end){SpannableString spannableString = new SpannableString(src);StrikethroughSpan strikethroughSpan = new StrikethroughSpan();spannableString.setSpan(strikethroughSpan, start,end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);return spannableString;}/**设置下划线* @param src* @param start* @param end* @return*/public static SpannableString setUnderline(String src,int start,int end){SpannableString spannableString = new SpannableString(src);UnderlineSpan underlineSpan = new UnderlineSpan();spannableString.setSpan(underlineSpan, start,end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);return spannableString;}/**同时设置下划线和中划线* @param src* @param up* @param down* @return*/public static SpannableString setUpDownLine(String src,String up,String down){int uPosition = src.indexOf(up);int dPosition = src.indexOf(down);SpannableString mSp = new SpannableString(src);UnderlineSpan underlineSpan = new UnderlineSpan();StrikethroughSpan strikethroughSpan = new StrikethroughSpan();if (uPosition >= 0) {mSp.setSpan(strikethroughSpan, uPosition,uPosition+up.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);}if (dPosition >= 0) {mSp.setSpan(underlineSpan, dPosition,dPosition+down.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);}return mSp;}/**设置上标* @param src* @param start* @param end* @return*/public static SpannableString setUpScript(String src,int start,int end){SpannableString spannableString = new SpannableString(src);SuperscriptSpan superscriptSpan = new SuperscriptSpan();spannableString.setSpan(superscriptSpan, start,end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);return spannableString;}/**设置下标* @param src* @param start* @param end* @return*/public static SpannableString setDownScript(String src,int start,int end){SpannableString spannableString = new SpannableString(src);SubscriptSpan subscriptSpan = new SubscriptSpan();spannableString.setSpan(subscriptSpan, start,end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);return spannableString;}/**设置部分文字加粗* @param src* @param start* @param end* @return*/public static SpannableString setTextBold(String src,int start,int end){SpannableString spannableString = new SpannableString(src);StyleSpan styleSpan_B = new StyleSpan(Typeface.BOLD);//StyleSpan styleSpan_I = new StyleSpan(Typeface.ITALIC);spannableString.setSpan(styleSpan_B, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);// spannableString.setSpan(styleSpan_I, 8, 10, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);// textView.setHighlightColor(Color.parseColor("#36969696"));// textView.setText(spannableString);return spannableString;}/**设置部分文字倾斜* @param src* @param start* @param end* @return*/public static SpannableString setTextItalic(String src,int start,int end){SpannableString spannableString = new SpannableString(src);//StyleSpan styleSpan_B = new StyleSpan(Typeface.BOLD);StyleSpan styleSpan_I = new StyleSpan(Typeface.ITALIC);//spannableString.setSpan(styleSpan_B, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);spannableString.setSpan(styleSpan_I, 8, 10, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);// textView.setHighlightColor(Color.parseColor("#36969696"));// textView.setText(spannableString);return spannableString;}public static SpannableString setTextIcon(String src, int start, int end, Drawable drawable){SpannableString spannableString = new SpannableString("在文本中添加表情(表情)");//Drawable drawable = getResources().getDrawable(R.mipmap.icon_one);drawable.setBounds(0, 0, 42, 42);ImageSpan imageSpan = new ImageSpan(drawable);spannableString.setSpan(imageSpan, 6, 8, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);return spannableString;}}
二、SpannableStringUtils,功能有重复
- 使用效果图

使用示例 ```java val create1 = SpannableStringUtils.getBuilder(“普通样式的文字,加粗”)
.setBold().create()
tv_textview_blod.text = create1
val create2 = SpannableStringUtils.getBuilder(“普通样式的文字,加粗倾斜”)
.setBoldItalic().create()
tv_textview_blodItalic.text = create2
val create3 = SpannableStringUtils.getBuilder(“普通样式的文字,下划线”)
.setUnderline().create()
tv_textview_three.text = create3
val create4 = SpannableStringUtils.getBuilder(“普通样式的文字,设置标识”)
.setFlag(SPAN_INCLUSIVE_EXCLUSIVE).create()
tv_textview_4.text = create4
val create5 = SpannableStringUtils.getBuilder(“普通样式的文字,设置foregroundcolor”)
.setForegroundColor(ContextCompat.getColor(this,R.color.red)).create()
tv_textview_5.text = create5
val create6 = SpannableStringUtils.getBuilder(“普通样式的文字,设置backgroundcolor”)
.setBackgroundColor(ContextCompat.getColor(this,R.color.red)).create()
tv_textview_6.text = create6
val create7 = SpannableStringUtils.getBuilder(“普通样式的文字,设置引用线颜色”)
.setQuoteColor(ContextCompat.getColor(this,R.color.red)).create()
tv_textview_7.text = create7
val create8 = SpannableStringUtils.getBuilder(“普通样式的文字,设置列表标记”)
.setBullet(20,ContextCompat.getColor(this,R.color.red)).create()
tv_textview_8.text = create8
val create9 = SpannableStringUtils.getBuilder(“普通样式的文字,设置字体比例”)
.setProportion(2f).create()
tv_textview_9.text = create9
val create10 = SpannableStringUtils.getBuilder(“普通样式的文字,设置字体横向比例”)
.setXProportion(2f).create()
tv_textview_10.text = create10
val create11 = SpannableStringUtils.getBuilder(“普通样式的文字,设置删除线”)
.setStrikethrough().create()
tv_textview_11.text = create11
val create12 = SpannableStringUtils.getBuilder(“普通样式的文字,设置上标”)
.setSuperscript().create()
tv_textview_12.text = create12
val create13 = SpannableStringUtils.getBuilder(“普通样式的文字,设置下标”)
.setSuperscript().create()
tv_textview_13.text = create13
//设置了图片以后就没有文字了 val create14 = SpannableStringUtils.getBuilder(“普通样式的文字,设置图片”)
.setResourceId(R.mipmap.ic_launcher).create()
//tv_textview_14.text = create14
//没什么效果,可能用法不对 tv_textview_15.movementMethod = LinkMovementMethod.getInstance() val create15 = SpannableStringUtils.getBuilder(“普通样式的文字,设置点击”)
.setClickSpan(object :ClickableSpan(){override fun onClick(widget: View) {Toast.makeText(this@TextViewActivity, "hello", Toast.LENGTH_SHORT).show()}}).create()
tv_textview_15.text = create15
val create16 = SpannableStringUtils.getBuilder(“普通样式的文字,设置超链接”)
.setForegroundColor(ContextCompat.getColor(this,R.color.red)).setUrl("https://www.baidu.com").create()
tv_textview_16.movementMethod = LinkMovementMethod.getInstance() tv_textview_16.text = create16 tv_textview_16.setTextColor(ContextCompat.getColor(this,R.color.red)) // tv_textview_16.setOnClickListener { // Toast.makeText(this@TextViewActivity, “hello”, Toast.LENGTH_SHORT).show() // }
- 3. 工具类```javapublic class SpannableStringUtils {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;@ColorIntprivate int foregroundColor;@ColorIntprivate int backgroundColor;@ColorIntprivate 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 Layout.Alignment align;private boolean imageIsBitmap;private Bitmap bitmap;private boolean imageIsDrawable;private Drawable drawable;private boolean imageIsUri;private Uri uri;private boolean imageIsResourceId;@DrawableResprivate int resourceId;private ClickableSpan clickSpan;private String url;private boolean isBlur;private float radius;private BlurMaskFilter.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>* Alignment#ALIGN_NORMAL}正常</li>* Alignment#ALIGN_OPPOSITE}相反</li>* Alignment#ALIGN_CENTER}居中</li>* </ul>* @return {@link Builder}*/public Builder setAlign(@Nullable Layout.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>* Blur # NORMAL}</li>* Blur #SOLID}</li>* Blur #OUTER}</li>* Blur #INNER}</li>* </ul>* @return {@link Builder}*/public Builder setBlur(float radius, BlurMaskFilter.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;}/*** 设置样式*/private void setSpan() {int start = mBuilder.length();mBuilder.append(this.text);int end = mBuilder.length();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(Utils.INSTANCE.getContext(), bitmap), start, end, flag);bitmap = null;imageIsBitmap = false;} else if (imageIsDrawable) {mBuilder.setSpan(new ImageSpan(drawable), start, end, flag);drawable = null;imageIsDrawable = false;} else if (imageIsUri) {mBuilder.setSpan(new ImageSpan(Utils.INSTANCE.getContext(), uri), start, end, flag);uri = null;imageIsUri = false;} else {mBuilder.setSpan(new ImageSpan(Utils.INSTANCE.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;}}}
