【Android】Span的使用

三三想成为安卓糕手

引入:通过上一章登录协议学习中SpannableString类的使用,我们对Span有了初步的认识;

现在新的问题来了,点击用户协议进行跳转和勾选用户协议两个事件共用了一个监听器,显然这是不合理的;本文深度学习一下span

一:Span家族

1:继承类梳理

在安卓开发中,Span是所有文本样式的基类,位于android.text.style.Span,是一个抽象类,里面的方法和属性用于控制文本的样式;在Span下,主要是由CharacterStyle,ParagraphStyle两个抽象类继承;抽象类下再继承一层,我们一般使用它们的子类;

CharacterStyle(字符级别样式) ------设置颜色,添加下划线。这种类型的Span继承自CharacterStyle,会让文本重新绘制,但不会重新测算布局

ParagraphStyle(段落级别样式)------影响段落的Span,更改代码块的对齐方式,外边距等等

子类名称 了解程度 CharacterStyle (抽象类) ParagraphStyle (抽象类) 说明
ForegroundColorSpan 用过 ✅ 继承 ❌ 不继承 设置文本的前景色(文字颜色)
BackgroundColorSpan 用过 ✅ 继承 ❌ 不继承 设置文本的背景颜色
StyleSpan 见过 ✅ 继承 ❌ 不继承 设置文本的字体样式(如粗体、斜体、正常等)
ClickableSpan 用过 ✅ 继承 ❌ 不继承 使文本具备点击事件能力(可自定义点击逻辑)
UnderlineSpan ✅ 继承 ❌ 不继承 为文本添加下划线
StrikethroughSpan ✅ 继承 ❌ 不继承 为文本添加删除线(中划线)
ScaleXSpan ✅ 继承 ❌ 不继承 使文本在水平方向拉伸或缩放
AlignmentSpan.Standard ❌ 不继承 ✅ 继承 设置段落的对齐方式(如左对齐、居中对齐、右对齐)
LeadingMarginSpan.Standard ❌ 不继承 ✅ 继承 设置段落的缩进(包括首行缩进和整体缩进)
LineHeightSpan.Standard ❌ 不继承 ✅ 继承 设置段落的行高(行间距)

2:常见的Span样式类

有很多都还不太熟悉,认识的认识,不认识的慢慢了解嘛~~

Span 样式类 使用频率 了解程度 作用描述 补充说明
BackgroundColorSpan 为部分文字设置背景颜色 可自定义颜色值,精准控制文字背景视觉效果
ForegroundColorSpan 为部分文字设置前景色(即文本颜色) 用于突出特定文字,灵活调整文字本身色彩
UnderlineSpan 为部分文字添加下划线 基础文本装饰,常用于强调可点击或特殊标识文字
StrikethroughSpan 为部分文字添加删除线(中划线) 常用来表示文本作废、价格对比等场景
TypefaceSpan 设置文本的字体 可指定系统字体或自定义字体,改变文字排版风格
TextAppearanceSpan 通过 style 定义文本样式,涵盖字体、大小、样式、颜色等 复用 XML 中 style 配置,统一文本外观规范
AbsoluteSizeSpan 设置文本的绝对字体大小 以具体数值(如 sp 单位)固定文字尺寸,适合明确字号需求的场景
RelativeSizeSpan 设置文本相对默认字体大小的缩放比例 基于原有字号动态调整,实现文字大小差异化展示
StyleSpan 设置文本字体样式(粗体、斜体等,通过 Typeface 常量控制,如 Typeface.BOLD 快速调整文字基础排版样式,搭配其他 Span 实现复杂效果
ClickableSpan(按钮) 让文本部分可点击,支持自定义点击事件 需结合 MovementMethod(如 LinkMovementMethod)才能生效,常用于交互文本
URLSpan 将文本设为超链接,点击打开指定 URL 需配置 setMovementMethod(LinkMovementMethod.getInstance()) 启用点击
MaskFilterSpan 为文本添加装饰效果(如模糊 BlurMaskFilter、浮雕 EmbossMaskFilter 实现特殊视觉效果,对性能有一定影响,需谨慎使用
ImageSpan 在文本中插入图片 可替换指定文字为图片,实现图文混排,需注意图片尺寸与文本基线对齐问题
ScaleXSpan 沿 X 轴缩放文本 拉伸或压缩文字水平方向显示,制造特殊排版效果
SubscriptSpan 设置下标文本(数学公式、化学分子式等场景常用,如 H₂O 中的 "₂" ) 使文字下沉到基线下方,满足专业符号排版需求
SuperscriptSpan 设置上标文本(数学公式、次方等场景常用,如 X² 中的 "²" ) 使文字上升到基线上方,适配公式、指数等排版
AlignmentSpan.Standard 设置段落级文本对齐方式(左对齐、右对齐、居中对齐等 ) 作用于段落,需结合支持段落排版的容器(如 TextView 配合换行符)

举例:可以看到我们的style包下有很多的关于span的类和接口

3:Span样式类用法

通过以上这些方法对文本进行处理后返回对应的类型(这里可以粗浅的理解成一个span)

这里举个简单的例子

Java 复制代码
		ForegroundColorSpan fColorSpan = new ForegroundColorSpan(Color.GREEN);
        spannableStringBuilder.setSpan(fColorSpan,7,13, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

最后传递给spannable.setSpan(Object what,int start,int end,int flags)中的第一个参数。

4:三种常用的包装类

以下这三个类都可以用来包装字符串,应用Span来设置文本样式,任君挑选。

具体的方法使用,遇到了在慢慢往里面补充(前方的路以后再来探索吧~~~)

可变文本 可变标记 数据结构 使用场景
SpannedString 不支持 不支持 线性数组 ①不准备在创建文本后修改标记
SpannableString 不支持 线性数组 ①将少量span(10个以内)附加到文本对象,并且文本为只读
SpannableStringBuilder 区间树 ①创建后需要修改文本,并且需要将span附加给文本 ②将大量span(10个以上)附加到文本对象(无论文本本身是否为只读)

二:代码实践

1:布局样式

简单定义一个TextVIew即可

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal"
    tools:context=".SpanActivity">

    <TextView
        android:id="@+id/tv_span"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

2:Java代码操作

java 复制代码
public class SpanActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_span);


        TextView tvSpan = findViewById(R.id.tv_span);

        //把文本转化为span进行一些设置
        String text = "你好,我是一个span测试字符";
        SpannableStringBuilder stringBuilder = new SpannableStringBuilder(text);
        ForegroundColorSpan fColorSpan = new ForegroundColorSpan(Color.GREEN);//设置文本颜色
        stringBuilder.setSpan(fColorSpan,7,13, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        //在下标3后开始插入hello
        stringBuilder.insert(3,"hello");
        //span与textView做关联
        tvSpan.setText(stringBuilder);

        //添加一个监听器
        tvSpan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stringBuilder.insert(8,"world");
                tvSpan.setText(stringBuilder);
            }
        });
    }
}

3:实现效果

在执行代码之前,检查一下跳转页面是否更新了,即第二个参数。

java 复制代码
btnTest.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent(MainActivity.this , SpanActivity.class);
                startActivity(intent);
                Log.i("info","hello");//打印日志
            }
        });

4:梳理步骤

①创建字符包装类传参文本

②设置span样式------可以有多个

③将字符包装类与TextVIew进行关联

注:每次字符包装类添加新的样式后,都需要作为参数传递给.setText(),让TextView进行更新

三:控件继续实践

1:RelativeSizeSpan

基于原来的大小,设置缩放,传入的参数 >1.0f是放大,<1.0f是缩小

会影响文本的行高、大小;继承关系如下

(1)xml中定义TextView

java 复制代码
    <TextView
        android:id="@+id/tv_re"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

(2)Java代码操作

java 复制代码
        TextView tv_re = findViewById(R.id.tv_re);
        String content = "我是一个测试文本增大50%的Span";
        SpannableString spannableString = new SpannableString(content);
        RelativeSizeSpan relativeSizeSpan = new RelativeSizeSpan(1.5f);
        spannableString.setSpan(relativeSizeSpan,3,6,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv_re.setText(spannableString);

(3)实现效果

2:QuoteSpan

Quote(引用、引述 )

创建一个实例,构造方法中接收的参数是一个32位int类型的参数,而非资源值的引用(也就是xml中定义的颜色值:R.color.purple)

(1)Java代码

java 复制代码
//        更改段落效果,在文本左侧添加一条绿色的竖线
//        QuoteSpan quoteSpan = new QuoteSpan(R.color.purple);//不能传参颜色的资源值
        QuoteSpan quoteSpan = new QuoteSpan(Color.GREEN);
        spannableString.setSpan(quoteSpan,6,8,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

使用QuoteSpan,只要用 QuoteSpan ,竖线一定显示在段落左侧;这里只跟文本中是否有\n(换行有关),与start和end的值无关

段落左侧是什么意思呢------就是换行符,大悟!!

(2)使用效果

3:marginLeft

增加段落左边距,在xml中的TextView中进行设置。这是为了让左侧的竖线显示出来,文本太靠左了。当然我们也可以设置文本居中。

xml 复制代码
android:layout_marginLeft="100dp"

效果

推荐绿色的

四:html标签添加文本样式

之前我们使用xml和java中set联动的方式来定义TextView属性,只能作用于一段文本(通过整个控件去设置整个文本的这个的效果),优点是生成的速度比较快,但是这种方式比较单一

于是我们就有了另外一个span的东西,通过span我们可以把一段文本的不同部分设置不同的效果啊,妙哉妙哉~

今天介绍第三种方式------html

1:font

对字体进行效果设置

(1)xml和java代码

xml 复制代码
    <TextView
        android:id="@+id/tv_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
java 复制代码
        //html设置字体,步骤也非常的清晰
        /**
         * ①找TextView
         * ②定String
         * ③转化为Span
         * ④把span设置给TextView
         */
        TextView test = findViewById(R.id.tv_test);
        String htmlString = "<font color=\"#1082ff\">我是蓝色的</font>";
        Spanned spanned = Html.fromHtml(htmlString, Html.FROM_HTML_MODE_COMPACT);
        test.setText(spanned);

(2)效果

(3)Html.fromHtml

重要代码解析------Html.fromHtml() 可以将 HTML 字符串转换为 Spanned 对象;

单参数方法已经被弃用了

flags标志参数分析(通过 Html.FROM_HTML_MODE_* 常量设置)

标志 重要单词 作用 适用版本
FROM_HTML_MODE_COMPACT compact紧凑的 紧凑模式,移除多余空白 API 24+
FROM_HTML_MODE_LEGACY legacy[ˈleɡəsi]传统的,旧版的 兼容旧版解析方式(默认) API 24+
FROM_HTML_OPTION_USE_CSS_COLORS 使用 CSS 颜色解析 API 32+