一、前言
基本包含了常见的设置加粗,倾斜,设置部分文字大小颜色等,除了没有加入替换图片功能,感觉替换图片对效果控制不够好,就没有加入。可以设置单个效果,也可以自由组合,同时设置多个效果,非常灵活。
二、使用效果和详解
- 部分文字加粗
使用方法,推荐使用方法一:
val src = "普通样式的文字测试富文本部分加粗"
val target1 = "样式"
val target2 = "测试"
//使用方法一
val ss1 = SpannableStringUtils.Builder(src,target1)
.isBold(true).build()
//使用方法二
val ss11 = SpannableStringUtils.ssb(src)
SpannableStringUtils.isBold(ss11,target2)
tv_textview_test.text = ss11
- 全部文字加粗
val src2 = "普通样式的文字测试富文本全部加粗"
val ss2 = SpannableStringUtils.Builder(src2)
.isBold(true).build()
tv_textview_blod.text = ss2
- 设置同一部分文字多种效果
加粗倾斜更换字体颜色
val src3 = "普通样式的文字测试富文本多种效果"
val target3 = "普通样式的文字"
val ss3 = SpannableStringUtils.Builder(src3,target3)
.isBoldItalic(true)
.foregroundColor(R.color.red)
.build()
tv_textview_blodItalic.text = ss3
- 设置多个部分效果
val src4 = "普通样式的文字测试富文本多个部分设置效果"
val target4 = "样式的"
val target44 = "测试富文本"
//设置 target4 部分
val ss4 = SpannableStringUtils.Builder(src4,target4)
.foregroundColor(R.color.red)
.build()
//再设置 target44 部分
SpannableStringUtils.foregroundColor(ss4,target44,R.color.blue_74D3FF)
tv_textview_three.text = ss4
- 设置部分文字点击,设置超链接同理,不过有些问题
val src5 = "普通样式的文字测试富文本部分文字可点击效果"
val target5 = "可点击效果"
val ss5 = SpannableStringUtils.Builder(src5,target5)
//.foregroundColor(R.color.red)
.clickSpan(object : ClickableSpan(){
override fun onClick(widget: View) {
//设置点击处理
Toast.makeText(this@TextViewActivity, "hello", Toast.LENGTH_SHORT).show()
}
})
.build()
//设置点击需要先添加view.setMovementMethod(LinkMovementMethod.getInstance())
tv_textview_4.movementMethod = LinkMovementMethod.getInstance()
tv_textview_4.text = ss5
三、所有方法详解
- 使用 build 初始化
textSize : 设置字体大小
clickSpan : 设置点击
url : 设置超链接
align : 设置对齐方式
fontFamily : 设置字体
isSubscript : 设置下标
isSuperscript : 设置上标
xProportion : 横向字体比例
proportion : 字体比例
start : 起始点,初始化时不想设置目标文字时使用
end : 结束点,初始化时不想设置目标文字时使用
isBold : 加粗
isItalic : 倾斜
isBoldItalic : 加粗和倾斜
isUnderline : 下划线
isStrikethrough : 删除线
foregroundColor : 文字颜色
backgroundColor : 背景色
quoteColor : 引用线颜色
- 使用 build 初始化
直接使用SpannableStringUtils时
isBold : 加粗
isItalic : 倾斜
isBoldItalic : 加粗和倾斜
isUnderline : 下划线
isStrikethrough : 删除线
foregroundColor : 文字颜色
backgroundColor : 背景色
textSize : 设置字体大小
clickSpan : 设置点击
url : 设置超链接
ssb : 获取一个SpannableStringBuilder
四、工具类
``` class SsbUtils private constructor() {
//针对多个部分调用设置使用呢 companion object { private const val flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
private fun getStart(ssb: SpannableStringBuilder, target: String?): Int {
if (target.isNullOrEmpty()) {
return 0
}
return ssb.indexOf(target)
}
private fun getEnd(ssb: SpannableStringBuilder, target: String?): Int {
if (target.isNullOrEmpty()) {
return 0
}
val pos = ssb.indexOf(target)
var end = pos + target.length
if (end > ssb.length) {
end = ssb.length
}
return end
}
private fun getPosStart(ssb: SpannableStringBuilder, start: Int): Int {
var posStart = start
if (start < 0) {
posStart = 0
} else if (start > ssb.length) {
posStart = ssb.length
}
return posStart
}
private fun getPosEnd(ssb: SpannableStringBuilder, end: Int): Int {
var posEnd = end
if (end > ssb.length) {
posEnd = ssb.length
}
return posEnd
}
fun isBold(ssb: SpannableStringBuilder, target: String?): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(
StyleSpan(Typeface.BOLD),
getStart(ssb, target),
getEnd(ssb, target),
flag
)
}
return ssb
}
fun isBold(ssb: SpannableStringBuilder, start: Int, end: Int): SpannableStringBuilder {
ssb.setSpan(
StyleSpan(Typeface.BOLD),
getPosStart(ssb, start),
getPosEnd(ssb, end),
flag
)
return ssb
}
fun isItalic(ssb: SpannableStringBuilder, target: String?): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(
StyleSpan(Typeface.ITALIC),
getStart(ssb, target),
getEnd(ssb, target),
flag
)
}
return ssb
}
fun isItalic(ssb: SpannableStringBuilder, start: Int, end: Int): SpannableStringBuilder {
ssb.setSpan(
StyleSpan(Typeface.ITALIC),
getPosStart(ssb, start),
getPosEnd(ssb, end),
flag
)
return ssb
}
fun isBoldItalic(ssb: SpannableStringBuilder, target: String?): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(
StyleSpan(Typeface.BOLD_ITALIC),
getStart(ssb, target),
getEnd(ssb, target),
flag
)
}
return ssb
}
fun isBoldItalic(
ssb: SpannableStringBuilder,
start: Int,
end: Int
): SpannableStringBuilder {
ssb.setSpan(
StyleSpan(Typeface.BOLD_ITALIC),
getPosStart(ssb, start),
getPosEnd(ssb, end),
flag
)
return ssb
}
fun isUnderline(ssb: SpannableStringBuilder, target: String?): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(UnderlineSpan(), getStart(ssb, target), getEnd(ssb, target), flag)
}
return ssb
}
fun isUnderline(ssb: SpannableStringBuilder, start: Int, end: Int): SpannableStringBuilder {
ssb.setSpan(UnderlineSpan(), getPosStart(ssb, start), getPosEnd(ssb, end), flag)
return ssb
}
fun isStrikethrough(ssb: SpannableStringBuilder, target: String?): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(StrikethroughSpan(), getStart(ssb, target), getEnd(ssb, target), flag)
}
return ssb
}
fun isStrikethrough(
ssb: SpannableStringBuilder,
start: Int,
end: Int
): SpannableStringBuilder {
ssb.setSpan(StrikethroughSpan(), getPosStart(ssb, start), getPosEnd(ssb, end), flag)
return ssb
}
fun foregroundColor(
ssb: SpannableStringBuilder,
target: String?,
@ColorRes froegroundColorRes: Int
): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(
ForegroundColorSpan(ResUtils.getColor(froegroundColorRes)),
getStart(ssb, target),
getEnd(ssb, target),
flag
)
}
return ssb
}
fun foregroundColor(
ssb: SpannableStringBuilder,
start: Int,
end: Int,
@ColorRes froegroundColorRes: Int
): SpannableStringBuilder {
ssb.setSpan(
ForegroundColorSpan(ResUtils.getColor(froegroundColorRes)),
getPosStart(ssb, start),
getPosEnd(ssb, end),
flag
)
return ssb
}
fun backgroundColor(
ssb: SpannableStringBuilder,
target: String?,
@ColorRes backgroundColor: Int
): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(
BackgroundColorSpan(ResUtils.getColor(backgroundColor)),
getStart(ssb, target),
getEnd(ssb, target),
flag
)
}
return ssb
}
fun backgroundColor(
ssb: SpannableStringBuilder,
start: Int,
end: Int,
@ColorRes backgroundColor: Int
): SpannableStringBuilder {
ssb.setSpan(
BackgroundColorSpan(ResUtils.getColor(backgroundColor)),
getPosStart(ssb, start),
getPosEnd(ssb, end),
flag
)
return ssb
}
fun textSize(
ssb: SpannableStringBuilder,
target: String?,
@DimenRes textSize: Int
): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(
AbsoluteSizeSpan(ResUtils.getDimensionPixelSize(textSize)),
getStart(ssb, target),
getEnd(ssb, target),
flag
)
}
return ssb
}
fun textSize(
ssb: SpannableStringBuilder,
start: Int,
end: Int,
@DimenRes textSize: Int
): SpannableStringBuilder {
ssb.setSpan(
AbsoluteSizeSpan(ResUtils.getDimensionPixelSize(textSize)),
getPosStart(ssb, start),
getPosEnd(ssb, end),
flag
)
return ssb
}
fun textSize(
ssb: SpannableStringBuilder,
target: String?,
textSize: Float
): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(
AbsoluteSizeSpan(DensityUtils.dp2px(textSize)),
getStart(ssb, target),
getEnd(ssb, target),
flag
)
}
return ssb
}
fun textSize(
ssb: SpannableStringBuilder,
start: Int,
end: Int,
textSize: Float
): SpannableStringBuilder {
ssb.setSpan(
AbsoluteSizeSpan(DensityUtils.dp2px(textSize)),
getPosStart(ssb, start),
getPosEnd(ssb, end),
flag
)
return ssb
}
fun clickSpan(
ssb: SpannableStringBuilder,
target: String?,
clickSpan: ClickableSpan
): SpannableStringBuilder {
if (!target.isNullOrEmpty()) {
ssb.setSpan(clickSpan, getStart(ssb, target), getEnd(ssb, target), flag)
}
return ssb
}
fun clickSpan(
ssb: SpannableStringBuilder,
start: Int,
end: Int,
clickSpan: ClickableSpan
): SpannableStringBuilder {
ssb.setSpan(clickSpan, getPosStart(ssb, start), getPosEnd(ssb, end), flag)
return ssb
}
fun url(
ssb: SpannableStringBuilder,
target: String?,
url: String?
): SpannableStringBuilder {
if (!target.isNullOrEmpty() && !url.isNullOrEmpty()) {
ssb.setSpan(URLSpan(url), getStart(ssb, target), getEnd(ssb, target), flag)
}
return ssb
}
fun url(
ssb: SpannableStringBuilder,
start: Int,
end: Int,
url: String?
): SpannableStringBuilder {
if (!url.isNullOrEmpty()) {
ssb.setSpan(URLSpan(url), getPosStart(ssb, start), getPosEnd(ssb, end), flag)
}
return ssb
}
fun ssb(src: String?): SpannableStringBuilder {
if (src.isNullOrEmpty()) {
return SpannableStringBuilder()
}
return SpannableStringBuilder(src)
}
}
//针对单个部分使用
class Builder(private val src: String, private val target: String? = null) {
private var ssb: SpannableStringBuilder = SpannableStringBuilder(src)
private var start: Int = 0
private var end: Int = 0
init {
start = getStart()
end = getEnd()
}
private val flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
private var isBold: Boolean = false//加粗
private var isItalic = false//倾斜
private var isBoldItalic = false//加粗倾斜
private var isUnderline = false//下划线
private var isStrikethrough = false//删除线
private var isSuperscript = false //上标
private var isSubscript = false //下标
@ColorInt
private var foregroundColor = 0 //前景色
@ColorInt
private var backgroundColor = 0 //背景色
@ColorInt
private var quoteColor = 0//引用线颜色
private var proportion = 0f //字体比例
private var xProportion = 0f //横向字体比例
/**
* 设置字体
*
* @param fontFamily 字体
* <ul>
* <li>monospace</li>
* <li>serif</li>
* <li>sans-serif</li>
* </ul>
* @return {@link Builder}
*/
private var fontFamily: String? = null
/**
* 设置对齐
*
* @param align 对其方式
* <ul>
* Alignment#ALIGN_NORMAL}正常</li>
* Alignment#ALIGN_OPPOSITE}相反</li>
* Alignment#ALIGN_CENTER}居中</li>
* </ul>
* @return {@link Builder}
*/
private var align: Layout.Alignment? = null
//<p>需添加view.setMovementMethod(LinkMovementMethod.getInstance())</p>
private var clickSpan: ClickableSpan? = null//点击事件
private var url: String? = null//超链接
private var textSize: Int = 0 //设置字体大小
fun textSize(textSize: Float): Builder {
this.textSize = DensityUtils.dp2px(textSize)
return this
}
fun textSize(@DimenRes textSize: Int): Builder {
this.textSize = ResUtils.getDimensionPixelSize(textSize)
return this
}
fun clickSpan(clickSpan: ClickableSpan): Builder {
this.clickSpan = clickSpan
return this
}
fun url(url: String): Builder {
this.url = url
return this
}
fun align(align: Layout.Alignment): Builder {
this.align = align
return this
}
fun fontFamily(fontFamily: String): Builder {
this.fontFamily = fontFamily
return this
}
fun isSubscript(isSubscript: Boolean): Builder {
this.isSubscript = isSubscript
return this
}
fun isSuperscript(isSuperscript: Boolean): Builder {
this.isSuperscript = isSuperscript
return this
}
fun xProportion(xProportion: Float): Builder {
this.xProportion = xProportion
return this
}
fun proportion(proportion: Float): Builder {
this.proportion = proportion
return this
}
private fun getStart(): Int {
var start = 0
if (src.isNullOrEmpty()) {
return start
}
if (!target.isNullOrEmpty()) {
start = src.indexOf(target)
}
if (start > ssb.length) {
start = ssb.length
}
return start
}
private fun getEnd(): Int {
var end = 0
if (src.isEmpty()) {
return end
}
end = if (!target.isNullOrEmpty()) {
src.indexOf(target) + target.length
} else {
ssb.length
}
if (end > ssb.length) {
end = ssb.length
}
return end
}
fun start(start: Int): Builder {
this.start = start
return this
}
fun end(end: Int): Builder {
this.end = end
return this
}
fun isBold(isBold: Boolean): Builder {
this.isBold = isBold
return this
}
fun isItalic(isItalic: Boolean): Builder {
this.isItalic = isItalic
return this
}
fun isBoldItalic(isBoldItalic: Boolean): Builder {
this.isBoldItalic = isBoldItalic
return this
}
fun isUnderline(isUnderline: Boolean): Builder {
this.isUnderline = isUnderline
return this
}
fun isStrikethrough(isStrikethrough: Boolean): Builder {
this.isStrikethrough = isStrikethrough
return this
}
fun foregroundColor(@ColorRes foregroundColor: Int): Builder {
this.foregroundColor = ResUtils.getColor(foregroundColor)
return this
}
fun backgroundColor(@ColorRes backgroundColor: Int): Builder {
this.backgroundColor = ResUtils.getColor(backgroundColor)
return this
}
fun quoteColor(@ColorRes quoteColor: Int): Builder {
this.quoteColor = ResUtils.getColor(quoteColor)
return this
}
fun build(): SpannableStringBuilder {
//设置所有
setSpan()
return ssb
}
private fun setSpan() {
//处理start,end 防止越界
if (start < 0) {
start = 0
} else if (start > ssb.length) {
start = ssb.length
}
if (end < 0) {
end = 0
} else if (end > ssb.length) {
end = ssb.length
}
if (isBold) {
ssb.setSpan(StyleSpan(Typeface.BOLD), start, end, flag)
}
if (isItalic) {
ssb.setSpan(StyleSpan(Typeface.ITALIC), start, end, flag)
}
if (isBoldItalic) {
ssb.setSpan(StyleSpan(Typeface.BOLD_ITALIC), start, end, flag)
}
if (isUnderline) {
ssb.setSpan(UnderlineSpan(), start, end, flag)
}
if (isStrikethrough) {
ssb.setSpan(StrikethroughSpan(), start, end, flag)
}
if (foregroundColor != 0) {
ssb.setSpan(ForegroundColorSpan(foregroundColor), start, end, flag)
}
if (backgroundColor != 0) {
ssb.setSpan(BackgroundColorSpan(backgroundColor), start, end, flag)
}
if (quoteColor != 0) {
ssb.setSpan(QuoteSpan(quoteColor), start, end, flag)
}
if (xProportion != 0f) {
ssb.setSpan(ScaleXSpan(xProportion), start, end, flag)
}
if (proportion != 0f) {
ssb.setSpan(RelativeSizeSpan(proportion), start, end, flag)
}
if (isSuperscript) {
ssb.setSpan(SuperscriptSpan(), start, end, flag)
}
if (isSubscript) {
ssb.setSpan(SubscriptSpan(), start, end, flag)
}
if (fontFamily != null) {
ssb.setSpan(TypefaceSpan(fontFamily), start, end, flag)
}
if (align != null) {
ssb.setSpan(AlignmentSpan.Standard(align!!), start, end, flag)
}
if (clickSpan != null) {
ssb.setSpan(clickSpan, start, end, flag)
}
if (url != null) {
ssb.setSpan(URLSpan(url), start, end, flag)
}
if (textSize != 0) {
ssb.setSpan(AbsoluteSizeSpan(textSize), start, end, flag)
}
}
}
}
<a name="AP8TI"></a>
## 五、专门针对 kotlin 封装
对很多功能都可以封装,简化使用,这里使用了扩展函数,更方便在Kotlin中使用,不过在Java中也可以使用,使用方法如下:
<a name="efe683e9"></a>
#### 第一种情况,要设置的内容已经是一段完整的内容
> 注意:链式调用时,只需要初始化第一个src就可以了,后续都会默认使用第一个,如果后续继续初始化src, 会导致前面的设置无效,只有最后一个生效。target和range都是为了确定要改变的文字的范围,两个初始化一个即可。
1. 对整个字符串设置效果
> src 和target默认等于TextView的text
//对整个 text 设置方式一,textView已经设置过内容,可以不用初始化src tvTvOne.sizeSpan(textSize = 20f) //对整个 text 设置方式二 tvTvOne2.typeSpan(src = “全部文字加粗”,target = “全部文字加粗”, type = SsbKtx.type_bold)
2. 设置部分文字效果
> type 有3个,对应加粗,倾斜,加粗倾斜
//设置部分文字效果 //tvTv2.typeSpan(range = 2..4,type = SsbKtx.type_bold) tvTv2.typeSpan(target = “部分”,type = SsbKtx.type_bold) //设置加粗倾斜效果 tvTv3.typeSpan(range = 0..4,type = SsbKtx.type_bold_italic)
3. 对同一个文字设置多个效果
> 对同一个部分做多种效果,只能第一个设置 src, 后续设置会导致前面的无效。
// tvTv4.typeSpan(range = 0..4,type = SsbKtx.type_bold_italic) // .foregroundColorIntSpan(range = 0..4,color = Color.GREEN) // .strikethroughSpan(range = 0..4) tvTv4.typeSpan(src = “只能这个可以设置 src,后面的再设置会导致前面效果无效”, range = 0..4,type = SsbKtx.type_bold_italic) .foregroundColorIntSpan(range = 0..4,color = Color.GREEN) .strikethroughSpan(range = 0..4)
4. 对多个不同的文字分别设置不同的效果
tvTv5.typeSpan(range = 0..4,type = SsbKtx.type_bold_italic) .foregroundColorIntSpan(range = 7..11,color = Color.BLUE)
5. 设置部分点击
tvTv6.clickIntSpan(range = 0..4){ Toast.makeText(this, “hello”, Toast.LENGTH_SHORT).show() }
6. 设置部分超链接
tvTv7.urlSpan(range = 0..4,url = “https://www.baidu.com“)
<a name="46068f3b"></a>
#### 第二种情况,拼接成一个完整的字符串
1. 拼接成完整的内容
tvTv8.text = “拼接一段文字” tvTv8.appendTypeSpan(“加粗”,SsbKtx.type_bold) .strikethroughSpan(target = “加粗”)//对同一部分文字做多个效果 .appendForegroundColorIntSpan(“改变字体颜色”,Color.RED)
1. 如果想对拼接的内容做多个效果,可以在其后面调用对应的方法,只要traget或是range正确即可。
<a name="6Hb8E"></a>
#### 完整代码
object SsbKtx { const val flag = SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE const val type_bold = Typeface.BOLD const val type_italic = Typeface.ITALIC const val type_bold_italic = Typeface.BOLD_ITALIC
} //—————————-CharSequence相关扩展———————————- /* CharSequence不为 null 或者 empty */ fun CharSequence?.isNotNullOrEmpty() = !isNullOrEmpty()
/* 获取一段文字在文字中的范围
- @param target
- @return */ fun CharSequence.range(target: CharSequence): IntRange { val start = this.indexOf(target.toString()) return start..(start + target.length) }
/* 将一段指定的文字改变大小
- @return
*/
fun CharSequence.sizeSpan(range: IntRange, textSize: Int): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(AbsoluteSizeSpan(textSize), range.first, range.last, SsbKtx.flag)
/* 将一段指定的文字,设置类型,是否加粗,倾斜
- @return
*/
fun CharSequence.typeSpan(range: IntRange, type: Int): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(StyleSpan(type), range.first, range.last, SsbKtx.flag)
/* 设置下划线
- @param range
- @return
*/
fun CharSequence.underlineSpan(range: IntRange): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(UnderlineSpan(), range.first, range.last, SsbKtx.flag)
/* 设置删除线
- @param range
- @return
*/
fun CharSequence.strikethroughSpan(range: IntRange): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(StrikethroughSpan(), range.first, range.last, SsbKtx.flag)
/* 设置文字颜色
- @param range
- @return
*/
fun CharSequence.foregroundColorSpan(range: IntRange, color: Int = Color.RED): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(ForegroundColorSpan(color), range.first, range.last, SsbKtx.flag)
/* 设置文字背景色
- @param range
- @return
*/
fun CharSequence.backgroundColorSpan(range: IntRange, color: Int = Color.RED): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(BackgroundColorSpan(color), range.first, range.last, SsbKtx.flag)
/* 设置引用线颜色
- @param range
- @return
*/
fun CharSequence.quoteColorSpan(range: IntRange, color: Int = Color.RED): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(QuoteSpan(color), range.first, range.last, SsbKtx.flag)
/* 设置字体大小比例
- @param range
- @return
*/
fun CharSequence.proportionSpan(range: IntRange, proportion: Float): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(RelativeSizeSpan(proportion), range.first, range.last, SsbKtx.flag)
/* 设置横向字体大小比例
- @param range
- @return
*/
fun CharSequence.proportionXSpan(range: IntRange, proportion: Float): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(ScaleXSpan(proportion), range.first, range.last, SsbKtx.flag)
/* 设置上标
- @param range
- @return
*/
fun CharSequence.superscriptSpan(range: IntRange): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(SuperscriptSpan(), range.first, range.last, SsbKtx.flag)
/* 设置下标
- @param range
- @return
*/
fun CharSequence.subscriptSpan(range: IntRange): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(SubscriptSpan(), range.first, range.last, SsbKtx.flag)
/* 设置字体
- @param range
- @return
*/
fun CharSequence.fontSpan(range: IntRange, font: String): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(TypefaceSpan(font), range.first, range.last, SsbKtx.flag)
@RequiresApi(Build.VERSION_CODES.P) fun CharSequence.fontSpan(range: IntRange, font: Typeface): CharSequence { return SpannableStringBuilder(this).apply { setSpan(TypefaceSpan(font), range.first, range.last, SsbKtx.flag) } }
/* 设置对齐方式
- @param range
- @return
*/
fun CharSequence.alignSpan(range: IntRange, align: Layout.Alignment): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(AlignmentSpan.Standard(align), range.first, range.last, SsbKtx.flag)
/* 设置url,超链接
- @param range
- @return
*/
fun CharSequence.urlSpan(range: IntRange, url: String): CharSequence {
return SpannableStringBuilder(this).apply {
} }setSpan(URLSpan(url), range.first, range.last, SsbKtx.flag)
/* 设置click,将一段文字中指定range的文字添加颜色和点击事件
- @param range
@return */ fun CharSequence.clickSpan( range: IntRange, color: Int = Color.RED, isUnderlineText: Boolean = false, clickAction: () -> Unit ): CharSequence { return SpannableString(this).apply {
val clickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
clickAction()
}
override fun updateDrawState(ds: TextPaint) {
ds.color = color
ds.isUnderlineText = isUnderlineText
}
}
setSpan(clickableSpan, range.first, range.last, SsbKtx.flag)
} }
//—————————-TextView相关扩展————————————— /* 设置目标文字大小, src,target 为空时,默认设置整个 text
- @return
*/
fun TextView?.sizeSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
@DimenRes textSize: Int
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
textSize == 0 -> this
range != null -> {
text = src.sizeSpan(range, ResUtils.getDimensionPixelSize(textSize))
this
}
target.isNotNullOrEmpty() -> {
text = src.sizeSpan(src.range(target!!), ResUtils.getDimensionPixelSize(textSize))
this
}
else -> this
/* 设置目标文字大小, src,target 为空时,默认设置整个 text
- @return
*/
fun TextView?.sizeSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
textSize: Float
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
textSize == 0f -> this
range != null -> {
text = src.sizeSpan(range, DensityUtils.dp2px(textSize))
this
}
target.isNotNullOrEmpty() -> {
text = src.sizeSpan(src.range(target!!), DensityUtils.dp2px(textSize))
this
}
else -> this
/* 追加内容设置字体大小
- @param str
- @param textSize
- @return
*/
fun TextView?.appendSizeSpan(str: String?, textSize: Float): TextView? {
str?.let {
} return this }this?.append(it.sizeSpan(0..it.length, DensityUtils.dp2px(textSize)))
fun TextView?.appendSizeSpan(str: String?, @DimenRes textSize: Int): TextView? { str?.let { this?.append(it.sizeSpan(0..it.length, ResUtils.getDimensionPixelSize(textSize))) } return this }
/* 设置目标文字类型(加粗,倾斜,加粗倾斜),src,target 为空时,默认设置整个 text
- @return
*/
fun TextView?.typeSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
type: Int
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.typeSpan(range, type)
this
}
target.isNotNullOrEmpty() -> {
text = src.typeSpan(src.range(target!!), type)
this
}
else -> this
fun TextView?.appendTypeSpan(str: String?, type: Int): TextView? { str?.let { this?.append(it.typeSpan(0..it.length, type)) } return this }
/* 设置目标文字下划线
- @return
*/
fun TextView?.underlineSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.underlineSpan(range)
this
}
target.isNotNullOrEmpty() -> {
text = src.underlineSpan(src.range(target!!))
this
}
else -> this
fun TextView?.appendUnderlineSpan(str: String?): TextView? { str?.let { this?.append(it.underlineSpan(0..it.length)) } return this }
/* 设置目标文字删除线
- @return
*/
fun TextView?.strikethroughSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.strikethroughSpan(range)
this
}
target.isNotNullOrEmpty() -> {
text = src.strikethroughSpan(src.range(target!!))
this
}
else -> this
fun TextView?.appendStrikethroughSpan(str: String?): TextView? { str?.let { this?.append(it.strikethroughSpan(0..it.length)) } return this }
/* 设置目标文字颜色
- @return
*/
fun TextView?.foregroundColorIntSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
color: Int
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.foregroundColorSpan(range, color)
this
}
target.isNotNullOrEmpty() -> {
text = src.foregroundColorSpan(src.range(target!!), color)
this
}
else -> this
fun TextView?.appendForegroundColorIntSpan(str: String?, color: Int): TextView? { str?.let { this?.append(it.foregroundColorSpan(0..it.length, color)) } return this }
/* 设置目标文字颜色
- @return
*/
fun TextView?.foregroundColorSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
@ColorRes color: Int
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.foregroundColorSpan(range, ResUtils.getColor(color))
this
}
target.isNotNullOrEmpty() -> {
text = src.foregroundColorSpan(src.range(target!!), ResUtils.getColor(color))
this
}
else -> this
fun TextView?.appendForegroundColorSpan(str: String?, @ColorRes color: Int): TextView? { str?.let { this?.append(it.foregroundColorSpan(0..it.length, ResUtils.getColor(color))) } return this }
/* 设置目标文字背景颜色
- @return
*/
fun TextView?.backgroundColorIntSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
color: Int
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.backgroundColorSpan(range, color)
this
}
target.isNotNullOrEmpty() -> {
text = src.backgroundColorSpan(src.range(target!!), color)
this
}
else -> this
fun TextView?.appendBackgroundColorIntSpan(str: String?, color: Int): TextView? { str?.let { this?.append(it.backgroundColorSpan(0..it.length, color)) } return this }
/* 设置目标文字背景颜色
- @return
*/
fun TextView?.backgroundColorSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
@ColorRes color: Int
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.backgroundColorSpan(range, ResUtils.getColor(color))
this
}
target.isNotNullOrEmpty() -> {
text = src.backgroundColorSpan(src.range(target!!), ResUtils.getColor(color))
this
}
else -> this
fun TextView?.appendBackgroundColorSpan(str: String?, @ColorRes color: Int): TextView? { str?.let { this?.append(it.backgroundColorSpan(0..it.length, ResUtils.getColor(color))) } return this }
/* 设置目标文字引用线颜色
- @return
*/
fun TextView?.quoteColorIntSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
color: Int
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.quoteColorSpan(range, color)
this
}
target.isNotNullOrEmpty() -> {
text = src.quoteColorSpan(src.range(target!!), color)
this
}
else -> this
fun TextView?.appendQuoteColorIntSpan(str: String?, color: Int): TextView? { str?.let { this?.append(it.quoteColorSpan(0..it.length, color)) } return this }
/* 设置目标文字引用线颜色
- @return
*/
fun TextView?.quoteColorSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
@ColorRes color: Int
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.quoteColorSpan(range, ResUtils.getColor(color))
this
}
target.isNotNullOrEmpty() -> {
text = src.quoteColorSpan(src.range(target!!), ResUtils.getColor(color))
this
}
else -> this
fun TextView?.appendQuoteColorSpan(str: String?, @ColorRes color: Int): TextView? { str?.let { this?.append(it.quoteColorSpan(0..it.length, ResUtils.getColor(color))) } return this }
/* 设置目标文字字体大小比例
- @return
*/
fun TextView?.proportionSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
proportion: Float
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.proportionSpan(range, proportion)
this
}
target.isNotNullOrEmpty() -> {
text = src.proportionSpan(src.range(target!!), proportion)
this
}
else -> this
fun TextView?.appendProportionSpan(str: String?, proportion: Float): TextView? { str?.let { this?.append(it.proportionSpan(0..it.length, proportion)) } return this }
/* 设置目标文字字体横向大小比例
- @return
*/
fun TextView?.proportionXSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
proportion: Float
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.proportionXSpan(range, proportion)
this
}
target.isNotNullOrEmpty() -> {
text = src.proportionXSpan(src.range(target!!), proportion)
this
}
else -> this
fun TextView?.appendProportionXSpan(str: String?, proportion: Float): TextView? { str?.let { this?.append(it.proportionXSpan(0..it.length, proportion)) } return this }
/* 设置目标文字字体上标
- @return
*/
fun TextView?.superscriptSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.superscriptSpan(range)
this
}
target.isNotNullOrEmpty() -> {
text = src.superscriptSpan(src.range(target!!))
this
}
else -> this
fun TextView?.appendSuperscriptSpan(str: String?): TextView? { str?.let { this?.append(it.superscriptSpan(0..it.length)) } return this }
/* 设置目标文字字体下标
- @return
*/
fun TextView?.subscriptSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.subscriptSpan(range)
this
}
target.isNotNullOrEmpty() -> {
text = src.subscriptSpan(src.range(target!!))
this
}
else -> this
fun TextView?.appendSubscriptSpan(str: String?): TextView? { str?.let { this?.append(it.subscriptSpan(0..it.length)) } return this }
/* 设置目标文字字体
- @return
*/
fun TextView?.fontSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
font: String
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.fontSpan(range, font)
this
}
target.isNotNullOrEmpty() -> {
text = src.fontSpan(src.range(target!!), font)
this
}
else -> this
fun TextView?.appendFontSpan(str: String?, font: String): TextView? { str?.let { this?.append(it.fontSpan(0..it.length, font)) } return this }
/* 设置目标文字字体
- @return
*/
@RequiresApi(Build.VERSION_CODES.P)
fun TextView?.fontSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
font: Typeface
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.fontSpan(range, font)
this
}
target.isNotNullOrEmpty() -> {
text = src.fontSpan(src.range(target!!), font)
this
}
else -> this
@RequiresApi(Build.VERSION_CODES.P) fun TextView?.appendFontSpan(str: String?, font: Typeface): TextView? { str?.let { this?.append(it.fontSpan(0..it.length, font)) } return this }
/* 设置目标文字对齐方式
- @return
*/
fun TextView?.alignSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
align: Layout.Alignment
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
text = src.alignSpan(range, align)
this
}
target.isNotNullOrEmpty() -> {
text = src.alignSpan(src.range(target!!), align)
this
}
else -> this
fun TextView?.appendAlignSpan(str: String?, align: Layout.Alignment): TextView? { str?.let { this?.append(it.alignSpan(0..it.length, align)) } return this }
/* 设置目标文字超链接
- @return
*/
fun TextView?.urlSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
url: String
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
movementMethod = LinkMovementMethod.getInstance()
text = src.urlSpan(range, url)
this
}
target.isNotNullOrEmpty() -> {
movementMethod = LinkMovementMethod.getInstance()
text = src.urlSpan(src.range(target!!), url)
this
}
else -> this
fun TextView?.appendUrlSpan(str: String?, url: String): TextView? { str?.let { this?.append(it.urlSpan(0..it.length, url)) } return this }
/* 设置目标文字点击
- @return
*/
fun TextView?.clickIntSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
color: Int = Color.RED,
isUnderlineText: Boolean = false,
clickAction: () -> Unit
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT // remove click bg color
text = src.clickSpan(range, color, isUnderlineText, clickAction)
this
}
target.isNotNullOrEmpty() -> {
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT // remove click bg color
text = src.clickSpan(src.range(target!!), color, isUnderlineText, clickAction)
this
}
else -> this
fun TextView?.appendClickIntSpan( str: String?, color: Int = Color.RED, isUnderlineText: Boolean = false, clickAction: () -> Unit ): TextView? { str?.let { this?.append(it.clickSpan(0..it.length, color, isUnderlineText, clickAction)) } return this }
/* 设置目标文字点击
- @return
*/
fun TextView?.clickSpan(
src: CharSequence? = this?.text,
target: CharSequence? = this?.text,
range: IntRange? = null,
@ColorRes color: Int,
isUnderlineText: Boolean = false,
clickAction: () -> Unit
): TextView? {
return when {
} }this == null -> this
src.isNullOrEmpty() -> this
target.isNullOrEmpty() && range == null -> this
range != null -> {
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT // remove click bg color
text = src.clickSpan(range, ResUtils.getColor(color), isUnderlineText, clickAction)
this
}
target.isNotNullOrEmpty() -> {
movementMethod = LinkMovementMethod.getInstance()
highlightColor = Color.TRANSPARENT // remove click bg color
text = src.clickSpan(
src.range(target!!),
ResUtils.getColor(color),
isUnderlineText,
clickAction
)
this
}
else -> this
fun TextView?.appendClickSpan( str: String?, @ColorRes color: Int, isUnderlineText: Boolean = false, clickAction: () -> Unit ): TextView? { str?.let { this?.append( it.clickSpan( 0..it.length, ResUtils.getColor(color), isUnderlineText, clickAction ) ) } return this } ``` 里面的ResUtils只是简单的获取资源文件,如果想直接引入,可以参考Github直接使用gradle依赖。