用Kotlin封装一个自定义SpannableString

 

参考自:https://github.com/jaychang0917/SimpleText

使用Kotlin进行了改写,保留了大部分功能,现不支持点击时的文字颜色和背景色(感觉用到的场景不多,所以就没加)

Github上有更详细介绍 :https://github.com/chenyucheng97/SpannableStringBuilder

欢迎star?

 

先看效果图:

用Kotlin封装一个自定义SpannableString_第1张图片

 

调用方法:

注意事项: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:

  • AbsoluteSizeSpan
  • RelativeSizeSpan
  • StyleSpan
  • TypefaceSpan
  • StrikethroughSpan
  • UnderlineSpan
  • BulletSpan
  • ForegroundColorSpan
  • SubscriptSpan
  • SuperscriptSpan
  • ClickableSpan

 

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 } }
  •  

     

    你可能感兴趣的:(Kotlin,Kotlin,Kotlin,SpannableString)