参考自:https://github.com/jaychang0917/SimpleText
使用Kotlin进行了改写,保留了大部分功能,现不支持点击时的文字颜色和背景色(感觉用到的场景不多,所以就没加)
Github上有更详细介绍 :https://github.com/chenyucheng97/SpannableStringBuilder
欢迎star?
先看效果图:
调用方法:
注意事项:SpannableString同时设置了ClickableSpan和ForegroundColorSpan后,发现ForegroundColorSpan不生效。
原因:ClickableSpan将ForegroundColorSpan的颜色覆盖了
解决方式:将ForegroundColorSpan替换为UnderlineSpan,并重写updateDrawState方法,参考:https://blog.csdn.net/Kikitious_Du/article/details/77170087
或者这里可以简化一下,先设置点击事件onClick(即先设置ClickableSpan),再设置textColor (即ForegroundColorSpan)
val content1 = MySpannableString(this, "前往下一步即表示您已阅读并接受Booking.com之条款及细则和隐私条款")
.first("条款及细则").size(25).onClick(tvContent) { showToast(this, "条款及细则") }.textColor(R.color.color_main)
.underline()
.first("隐私条款").size(25).onClick(tvContent) { showToast(this, "隐私条款") }.textColor(R.color.color_main)
.underline()
.first("下一步").strikethrough().bold().scaleSize(2)
tvContent.text = content1
val content2 = MySpannableString(this, "Booking.com之条款及细则和隐私条款")
.bullet(40, R.color.black)
textView.text = content2
支持的Span:
MySpannableString源码:
/**
* Created by cyc on 2018/11/5.
*
* 常用Span封装,不支持点击时文字颜色和背景色的设置(使用场景少,暂不添加)
* 支持设置字体颜色、大小、下划线、删除线、点击事件 等,
* 可以对一个字符串中的多个目标子串进行设置Span
*
*/
class MySpannableString(private val context: Context, text: CharSequence) : SpannableString(text) {
private val spanMode = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
// 初始时,待处理的索引范围为全部字符串
private val rangeList = mutableListOf(Pair(0, text.length))
private var textColor: Int = 0
/**
* 匹配出现的第一个目标子串[target],并记录开始和结束的index
*/
fun first(target: String): MySpannableString {
rangeList.clear()
val index = toString().indexOf(target)
val range = Pair(index, index + target.length)
rangeList.add(range)
return this
}
/**
* 匹配出现的最后一个目标子串[target],并记录开始和结束的index
*/
fun last(target: String): MySpannableString {
rangeList.clear()
val index = toString().lastIndexOf(target)
val range = Pair(index, index + target.length)
rangeList.add(range)
return this
}
/**
* 匹配出现的所有目标子串[target],并记录开始和结束的index
*/
fun all(target: String): MySpannableString {
rangeList.clear()
val indexes = indexesOf(toString(), target)
for (index in indexes) {
val range = Pair(index, index + target.length)
rangeList.add(range)
}
return this
}
/**
* 记录源字符串[src]中目标子串 [target]出现的索引位置
*/
fun indexesOf(src: String, target: String): MutableList {
val positions = mutableListOf()
var index = src.indexOf(target)
while (index >= 0) {
positions.add(index)
index = src.indexOf(target, index + 1)
}
return positions
}
/**
* 手动输入一个起点索引[from]和终点索引[to]
*/
fun range(from: Int, to: Int): MySpannableString {
rangeList.clear()
val range = Pair(from, to + 1)
rangeList.add(range)
return this
}
/**
* 手动输入所有起点和终点的索引范围[ranges]
*/
fun ranges(ranges: MutableList>): MySpannableString {
rangeList.clear()
rangeList.addAll(ranges)
return this
}
/**
* 计算两个字符串[startText] 和 [endText]之间的字符串的索引,加入到待处理的集合中,后续的Span设置都是对该索引范围内的字串进行的
*/
fun between(startText: String, endText: String): MySpannableString {
rangeList.clear()
val startIndex = toString().indexOf(startText) + startText.length + 1
val endIndex = toString().lastIndexOf(endText) - 1
val range = Pair(startIndex, endIndex)
rangeList.add(range)
return this
}
/**
* 给target字串设置文字绝对大小为[dp]
*/
fun size(dp: Int): MySpannableString {
for (range in rangeList) {
setSpan(AbsoluteSizeSpan(dp, true), range.first, range.second, spanMode)
}
return this
}
/**
* 给target字串设置文字相对大小,指相对于文本设定的大小的相对比例为[proportion]
*/
fun scaleSize(proportion: Int): MySpannableString {
for (range in rangeList) {
setSpan(RelativeSizeSpan(proportion.toFloat()), range.first, range.second, spanMode)
}
return this
}
/**
* 给target字串设置样式(粗体)
*/
fun bold(): MySpannableString {
for (range in rangeList) {
setSpan(StyleSpan(Typeface.BOLD), range.first, range.second, spanMode)
}
return this
}
/**
* 给target字串设置样式(斜体)
*/
fun italic(): MySpannableString {
for (range in rangeList) {
setSpan(StyleSpan(Typeface.ITALIC), range.first, range.second, spanMode)
}
return this
}
/**
* 给target字串设置样式(正常)
*/
fun normal(): MySpannableString {
for (range in rangeList) {
setSpan(StyleSpan(Typeface.NORMAL), range.first, range.second, spanMode)
}
return this
}
/**
* 给target字串设置样式(粗斜体)
*/
fun bold_italic(): MySpannableString {
for (range in rangeList) {
setSpan(StyleSpan(Typeface.BOLD_ITALIC), range.first, range.second, spanMode)
}
return this
}
/**
* 字体样式,可以设置不同的字体,比如系统自带的SANS_SERIF、MONOSPACE和SERIF
*/
fun font(font: String): MySpannableString {
for (range in rangeList) {
setSpan(TypefaceSpan(font), range.first, range.second, spanMode)
}
return this
}
/**
* 给target字串添加删除线
*/
fun strikethrough(): MySpannableString {
for (range in rangeList) {
setSpan(StrikethroughSpan(), range.first, range.second, spanMode)
}
return this
}
/**
* 给target字串添加下划线
*/
fun underline(): MySpannableString {
for (range in rangeList) {
setSpan(UnderlineSpan(), range.first, range.second, spanMode)
}
return this
}
/**
* 类似于HTML中的标签的圆点效果,[dp]表示圆点和字体的间距,[colorRes]表示圆点的颜色
*/
fun bullet(dp: Int, @ColorRes colorRes: Int?): MySpannableString {
for (range in rangeList) {
setSpan(BulletSpan(dp, colorRes ?: textColor), range.first, range.second, spanMode)
}
return this
}
/**
* 字体颜色 [colorRes]表示target字串的字体颜色
*/
fun textColor(@ColorRes colorRes: Int): MySpannableString {
textColor = ContextCompat.getColor(context, colorRes)
for (range in rangeList) {
setSpan(ForegroundColorSpan(textColor), range.first, range.second, spanMode)
}
return this
}
/**
* 将target字串作为下标
*/
fun subscript(): MySpannableString {
for (range in rangeList) {
setSpan(SubscriptSpan(), range.first, range.second, spanMode)
}
return this
}
/**
* 将target字串作为上标
*/
fun superscript(): MySpannableString {
for (range in rangeList) {
setSpan(SuperscriptSpan(), range.first, range.second, spanMode)
}
return this
}
/**
* 给[textView]设置一个点击事件[onTextClickListener]
*/
fun onClick(textView: TextView, onTextClickListener: () -> Unit): MySpannableString {
for (range in rangeList) {
val span = object : ClickableSpan() {
override fun onClick(widget: View?) {
onTextClickListener.invoke()
}
}
setSpan(span, range.first, range.second, spanMode)
}
textView.highlightColor = Color.TRANSPARENT
textView.movementMethod = LinkMovementMethod.getInstance()
return this
}
}