一 写在最初

上一篇文章中介绍了一些 Span理论知识,包含

  • Span 的定义、分类;
  • 富文本处理类:SpannedString、SpannableString 、SpannableStringBuilder
  • setSpan(Object what, int start, int end, int flags)中各个参数的含义及运用示例。

文章地址:超能力文字:探究Span机制的多彩国际(一),本篇继续经过示例介绍各个Span的用法。坐稳了,开端发车

二 Span示例

文本如下:

      private const val SPAN_STR: String =
             "北国风光,千里冰封,万里雪飘。\n\n" +
             "望长城表里,惟余莽莽;大河上下,顿失滔滔。\n\n山舞银蛇,原驰蜡象,欲与天公试比高。\n\n" +
             "须晴日,看红装素裹,_ 分外妖娆。\n\n江山如此多娇,引很多英豪竞折腰。\n\n" +
             "惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。\n\n" +
              "一代天骄,成吉思汗,只识弯弓射大雕。\n\n" + 
              "俱往矣,数风流人物,还看今朝。"
        const val SPAN_STR2 = "锄禾日当午,\n\t汗滴禾下土。\n谁知盘中餐,\n\t粒粒皆辛苦。"
        const val SEG_1 = "北国风光"
        const val SEG_2 = "千里冰封"
        const val SEG_3 = "万里雪飘"
        const val SEG_4 = "望长城表里"
        const val SEG_5 = "惟余莽莽"
        const val SEG_6 = "大河上下"
        const val SEG_7 = "顿失滔滔"
        const val SEG_8 = "山舞银蛇"
        const val SEG_9 = "原驰蜡象"
        const val SEG_10 = "欲与天公试比高"
        const val SEG_11 = "须晴日"
        const val SEG_12 = "看红装素裹"
        const val SEG_13 = "_ 分外妖娆"
        const val SEG_14 = "江山如此多娇"
        const val SEG_15 = "引很多英豪竞折腰"
        const val SEG_16 = "惜秦皇汉武"
        const val SEG_17 = "略输文采"
        const val SEG_18 = "唐宗宋祖"
        const val SEG_19 = "稍逊风骚"
        const val SEG_20 = "一代天骄"
        const val SEG_21 = "成吉思汗"
        const val SEG_22 = "只识弯弓射大雕"
        const val SEG_23 = "俱往矣"
        const val SEG_24 = "数风流人物"
        const val SEG_25 = "还看今朝。"

经过 TextView 原始展现如下:

val spanBuilder = SpannableStringBuilder(SPAN_STR)
tvSpan.text = spanBuilder

超能力文本:探索Span机制的多彩世界(二)

2.1 影响外观的Span

超能力文本:探索Span机制的多彩世界(二)
全体示例作用图:

超能力文本:探索Span机制的多彩世界(二)

2.1.1、ForegroundColorSpan

字体色彩款式,用于改动字体色彩

spanBuilder.setSpan(
            ForegroundColorSpan(Color.RED),
            index1, index1 + SEG_1.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

作用图:

超能力文本:探索Span机制的多彩世界(二)

2.1.2、BackgroundColorSpan

背景色款式,能够用来设定文本的背景色;

spanBuilder.setSpan(
            BackgroundColorSpan(Color.GRAY),
            index2, index2 + SEG_2.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

作用图:

超能力文本:探索Span机制的多彩世界(二)

2.1.3、UnderlineSpan

下划线款式,给文本增加下划线;

spanBuilder.setSpan(
     UnderlineSpan(), index3, index3 + SEG_3.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
   )

作用图:

超能力文本:探索Span机制的多彩世界(二)

2.1.4、MaskFilterSpan

运用指定的 MaskFilter 到文本,能够完成一些特殊的作用,如含糊(BlurMaskFilter)等。

MaskFilterSpan(MaskFilter filter)设置滤镜Span,参数filter是MaskFilter类型。 BlurMaskFilter是MaskFilter子类,能够运用于Paint的一个滤镜。能够将制作的图形进行含糊处理,以达到一些特定的视觉作用。
1、radius : 含糊半径。数值越大,含糊的范围越广。
2、style是BlurMaskFilter.Blur枚举,表示含糊的类型,包含以下四种:
— a、NORMAL:普通含糊,运用于整个图画。
— b、SOLID:只制作含糊的区域,其他区域为通明。
— c、OUTER:外部含糊,只在图画外部含糊。
— d、INNER:内部含糊,只在图画内部含糊。

 spanBuilder.setSpan(
            //1、NORMAL:在原始鸿沟表里都进行含糊处理。
            MaskFilterSpan(BlurMaskFilter(20F, BlurMaskFilter.Blur.NORMAL)),
            index4, index4 + SEG_4.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
 spanBuilder.setSpan(
            //2、SOLID:在原始鸿沟内部填充实心色彩,在鸿沟外进行含糊处理。
            MaskFilterSpan(BlurMaskFilter(20F, BlurMaskFilter.Blur.SOLID)),
            index5, index5 + SEG_5.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
 spanBuilder.setSpan(
            //3、OUTER:在鸿沟内部不进行制作,在鸿沟外进行含糊处理。
            MaskFilterSpan(BlurMaskFilter(2F, BlurMaskFilter.Blur.OUTER)),
            index6, index6 + SEG_6.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
 spanBuilder.setSpan(
            //4、INNER:在鸿沟内部进行含糊处理,在鸿沟外不进行制作。
            MaskFilterSpan(BlurMaskFilter(20F, BlurMaskFilter.Blur.INNER)),
            index7, index7 + SEG_7.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.1.5、StrikethroughSpan

删除线款式,给文本增加删除线作用;

spanBuilder.setSpan(
            StrikethroughSpan(),
            index8, index8 + SEG_8.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
 )

作用图:

超能力文本:探索Span机制的多彩世界(二)

2.1.6、ClickableSpan

设置点击事情,留意点击相关的有必要设置textView.movementMethod = LinkMovementMethod.getInstance(),不然点击不收效。

spanBuilder.setSpan(
      CustomClickSpan {showToast("自定义ClickableSpan点击: $SEG_9") },
      index9, index9 + SEG_9.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
  )
//自定义ClickableSpan
class CustomClickSpan(private val click: (View) -> Unit) : ClickableSpan() {
    override fun onClick(widget: View) {
        click.invoke(widget)
    }
    /**
     * 经过修正TextPaint来修正文本状态,如修正文本色彩、下划线是否展现等
     * @param paint
     */
    override fun updateDrawState(paint: TextPaint) {
        paint.color = Color.BLUE
        paint.isUnderlineText = false
    }
}

经过自定义 ClickableSpan 并重写 updateDrawState() 修正 TextPaint ,去掉了默许的下划线、修正了文字色彩。履行成果:

超能力文本:探索Span机制的多彩世界(二)
,点击文字时就会触发 onClick 回调了。

2.1.7、URLSpan

点击文字时进行链接跳转

spanBuilder.setSpan(
        URLSpan("https://www.baidu.com"),
        index10, index10 + SEG_10.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
   )

作用图:

超能力文本:探索Span机制的多彩世界(二)

2.2 影响衡量的Span

超能力文本:探索Span机制的多彩世界(二)
全体示例作用:

超能力文本:探索Span机制的多彩世界(二)

2.2.1、StyleSpan

字体款式,如经过传入的 Typeface 设置正常、粗体、斜体、加粗并歪斜等款式;

//设置StyleSpan:加粗、斜体等,如Typeface.NORMAL、BOLD、ITALIC、BOLD_ITALIC
spanBuilder.setSpan(
            StyleSpan(Typeface.BOLD_ITALIC),
            index11, index11 + SEG_11.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.2.2、TypefaceSpan

设置不同款式的文本字体

val customTypeface = Typeface.create(
            ResourcesCompat.getFont(this, R.font.iconfont_guide),
            Typeface.BOLD_ITALIC
        )
/**
 * TypefaceSpan结构函数有两个:
 * 1、TypefaceSpan(String family)字体族,终究经过Typeface.create(family, style)创立Typeface
 * 2、TypefaceSpan(Typeface typeface)直接传入Typeface
 * 当经过TypefaceSpan(String family)创立时,曾经的Typeface都会保留并与新创立的合并;而经过TypefaceSpan(Typeface typeface)创立的会直接掩盖之前的Typeface设置。
*/
spanBuilder.setSpan(
            //TypefaceSpan(customTypeface), //API>=28以上才有
            TypefaceSpan("serif"), //体系自带的有normal,sans,monospace,serif
            index12, index12 + SEG_12.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.2.3、ImageSpan

图片款式,用于在文本中刺进图片;

val imgDrawable =
    ResourcesCompat.getDrawable(resources, R.drawable.icon_flower, null)
imgDrawable?.let { 
    it.setBounds(0, 0, 40.dp2px(), 40.dp2px())
    spanBuilder.setSpan(
           CenterImageSpan(it),
           index13, index13 + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
       )
 }
//自定义ImageSpan
class CenterImageSpan(drawable: Drawable, verticalAlignment: Int = ALIGN_CENTER) :
    ImageSpan(drawable, verticalAlignment) {
 private var mDrawableRef: WeakReference<Drawable>? = null
    /**
     * @param paint Paint instance.
     * @param text Current text.
     * @param start Start character index for span.
     * @param end End character index for span.
     * @param fm Font metrics, can be null.
     * @return Width of the span.
     */
override fun getSize(
        paint: Paint,
        text: CharSequence?,
        start: Int,
        end: Int,
        fm: Paint.FontMetricsInt?,
    ): Int {
        val d = getCachedDrawable()
        val rect = d?.bounds
        rect?.let {
            if (fm != null) {
                val fmInt = paint.fontMetricsInt
                val imgHeight = d.bounds.height() //图片高度
                val textHeight = fm.bottom - fm.top //文字行高度
                val halfDiffer = (imgHeight - textHeight) / 2
                if (imgHeight > textHeight && verticalAlignment == ALIGN_CENTER) {
                    fm.ascent = fmInt.ascent - halfDiffer
                    fm.descent = fmInt.descent + halfDiffer
                    fm.top = fmInt.top - halfDiffer
                    fm.bottom = fmInt.bottom + halfDiffer
                } else {
                    fm.ascent = -rect.bottom
                    fm.descent = 0
                    fm.top = fm.ascent
                    fm.bottom = 0
                }
            }
            return rect.right
        }
        return 0
    }
private fun getCachedDrawable(): Drawable? {
        val wr: WeakReference<Drawable>? = mDrawableRef
        var d: Drawable? = null
        if (wr != null) {
            d = wr.get()
        }
        if (d == null) {
            d = drawable
            mDrawableRef = WeakReference<Drawable>(d)
        }
        return d
    }
}

履行成果:

超能力文本:探索Span机制的多彩世界(二)

留意,这里运用的是自定义ImageSpan,目的是不论图片的巨细,图片都能够正常的和文字进行居中展现;而假如运用默许ImageSpan时,假如图片的高度大于文字自身,那么居中展现就会失效。

2.2.4、RelativeSizeSpan

相对巨细款式,相对于原始巨细调整文本的巨细;

//自定义RelativeSizeSpan
spanBuilder.setSpan(
         RelativeSizeColorSpan(0.8f, Color.RED),
         index14, index14 + SEG_14.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
 )
/**
 * 自定义Span
 * @param size : 文本被缩放的份额
 */
class RelativeSizeColorSpan(size: Float, @ColorInt val color: Int = Color.GRAY) :
    RelativeSizeSpan(size) {
    /**
     * 在制作文本时更新款式的制作状态,例如文本色彩、下划线等。
     */
    override fun updateDrawState(paint: TextPaint) {
        super.updateDrawState(paint)
        paint.color = color
        paint.isUnderlineText = true
    }
}

这里不仅想改动巨细,还想改动款式(文本色彩、下划线),所以直接经过简略的自定义RelativeSizeSpan,然后在复写的 updateDrawState() 里设置即可,履行成果(箭头方向):

超能力文本:探索Span机制的多彩世界(二)

2.2.5、AbsoluteSizeSpan

绝对巨细款式,以指定的像素巨细设置文本的巨细;

 spanBuilder.setSpan(
            /**
             * @param size 文本的绝对值巨细,单位是像素px
             * @param dip  假如为true,则size变认为dp为单位;不然还是以像素px为单位。
             */
            AbsoluteSizeSpan(20, true),
            index15, index15 + SEG_15.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
   )

履行成果(箭头方向):

超能力文本:探索Span机制的多彩世界(二)

2.2.6、ScaleXSpan

在水平方向上缩放文本;

spanBuilder.setSpan(
            ScaleXSpan(2f),
            index16, index16 + SEG_16.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.2.7、SubscriptSpan

将文本baseline的方位向下移动,用于显现下标文本;

//将文本baseline的方位向下移动
spanBuilder.setSpan(
     SubscriptSpan(),
     index17 + SEG_17.length / 2, index17 + SEG_17.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
  )

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.2.8、SuperscriptSpan

将文本baseline的方位向上移动,用于显现上标文本;

//将文本baseline的方位向上移动
spanBuilder.setSpan(
    SuperscriptSpan(),
     index18 + SEG_18.length / 2, index18 + SEG_18.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
 )

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.2.9、TextAppearanceSpan

设置文本字体、巨细、款式和色彩等

spanBuilder.setSpan(
            TextAppearanceSpan(this, R.style.textAppearanceSpan),
            index19, index19 + SEG_19.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
//styles.xml
<resources>
    <style name="textAppearanceSpan" parent="@android:style/TextAppearance">
        <item name="android:textColor">@color/result_points</item>
        <item name="android:textSize">20sp</item>
        <!-- italic、bold、normal -->
        <item name="android:textStyle">italic</item>
        <!-- monospace、serif、sans-serif -->
        <item name="fontFamily">serif</item>
    </style>
<resources>

履行成果(箭头方向):

超能力文本:探索Span机制的多彩世界(二)

2.3 影响阶段的Span

超能力文本:探索Span机制的多彩世界(二)

2.3.1、LeadingMarginSpan

阶段缩进,即为文本增加指定的行首缩进;

spanBuilder.setSpan(
            /**
             * first: 每个阶段的首行缩进
             * mRest: 每个阶段的其他行缩进
             */
            LeadingMarginSpan.Standard(20.dp2px(), 5.dp2px()),
            0, SPAN_STR.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
  )

履行成果:

超能力文本:探索Span机制的多彩世界(二)
能够看到每个阶段的首行都所进行20dp的缩进,由于每个阶段都只要一行,所以针对一个阶段中mRest 其他行的缩进没有表现出来而已。

2.3.2、LineBackgroundSpan

LineBackgroundSpan改动行的背景色,假如[start,end)不行一行按一行处理

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    spanBuilder.setSpan(
                LineBackgroundSpan.Standard(Color.YELLOW),
                0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
      )
}

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.3.3、LineHeightSpan

改动阶段的行高,可用于调整行与行之间的间隔。留意,LineHeightSpan 改动的是整个阶段的行高,即便它只掩盖阶段的一部分。

//LineHeightSpan改动阶段的行高
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    //留意,LineHeightSpan将改动整个阶段的行高,即便它只掩盖阶段的一部分。
    spanBuilder.setSpan(
           LineHeightSpan.Standard(20.dp2px()),
           0, SPAN_STR2.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
      )
 }

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.3.4、DrawableMarginSpan

在文本的行首增加一个带有指定边距的 Drawable

val imgDrawable = ResourcesCompat.getDrawable(resources, R.drawable.icon_arrow_pull, null)
spanBuilder.setSpan(
            DrawableMarginSpan(imgDrawable!!, 10.sp2px()),
            0, SPAN_STR.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.3.5、IconMarginSpan

IconMarginSpanDrawableMarginSpan类似,在文本的行首增加一个带有指定边距的Bitmap。

val bitmap = BitmapFactory.decodeResource(resources, R.drawable.icon_arrow_pull), 10.sp2px()
spanBuilder.setSpan(
     IconMarginSpan(bitmap),
     0, SPAN_STR.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

超能力文本:探索Span机制的多彩世界(二)

2.3.6、QuoteSpan

QuoteSpan 能够在文本开端的当地增加引证款式(一个笔直的线条)。

val quoteSpan = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            /**
             * QuoteSpan(int color, int stripeWidth, int gapWidth)
             * @param color 笔直线条色彩。缺省状况下,条带色彩为0xff0000ff
             * @param stripeWidth 线条的宽度,以像素为单位。默许值是2px。
             * @param gapWidth 线条和阶段之间的间隔,以像素为单位。默许值是2px。
             */
            QuoteSpan(Color.RED, 20, 40)
        } else {
            QuoteSpan(Color.RED)
        }
spanBuilder.setSpan(quoteSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.3.7、BulletSpan

给文本增加项目符号(圆点款式)

private fun getBulletSpan(): BulletSpan {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            /**
             * BulletSpan(int gapWidth, int color, int bulletRadius)
             * @param gapWidth: 项目符号和阶段之间的间隔,单位是px,默许2px
             * @param color:项目符号色彩,默许是文本色彩
             * @param bulletRadius:符号半径,单位是px,默许是4px
             */
            BulletSpan(20, Color.RED, 10)
        } else {
            BulletSpan(20, Color.RED)
        }
    }
//榜首行设置圆点
spanBuilder.setSpan(getBulletSpan(), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
val index23 = SPAN_STR.indexOf(SEG_23)
//最终一行设置圆点
spanBuilder.setSpan(getBulletSpan(), 
     index23, index23 + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)

履行成果:

超能力文本:探索Span机制的多彩世界(二)

2.3.8、AlignmentSpan

文本对齐方法,AlignmentSpan.Standard是其默许完成,其结构函数AlignmentSpan.Standard(Layout.Alignment align) 中的 alignLayout.Alignment枚举类型参数,包含三种状况:ALIGN_CENTER居中、 ALIGN_NORMAL正常、ALIGN_OPPOSITE相反方向。如:

spanBuilder.setSpan(
    AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0, SPAN_STR.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 
 )

超能力文本:探索Span机制的多彩世界(二)
能够看到文本已经居中展现了。

2.3.9、TabStopSpan

阶段中榜首行的左侧偏移量(与\t有联系),默许完成为TabStopSpan.Standard

val SPAN_STR2 = "锄禾日当午,\n\t汗滴禾下土。\n谁知盘中餐,\n\t粒粒皆辛苦。"
val spanBuilder = SpannableStringBuilder(SPAN_STR2)
spanBuilder.setSpan(TabStopSpan.Standard(40.dp2px()), 
         0, SPAN_STR2.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
     )
tvSpan.text = spanBuilder     

履行成果:

超能力文本:探索Span机制的多彩世界(二)