Android-TextView设置多种颜色及部分点击事件

前言

最近项目中有一个小小的细节,就是一般温馨提示语,一段文字,但是中间会包含一些字体可点击,有特殊颜色,比如下图的温馨提示:

这里写图片描述

一般实现方式有以下几种:

1.TextView 拼接
2.加载 Html
3.使用 SpannableString

其中的第一种方法,这里就不考虑了,因为这里也不确定字体到底占几行,并且链接文字位置也不可控
那下面就来看一下后面两种的实现方式。

用Html实现

这里主要就是用 html 语句。然后设置 TextView 即可。如:

String html = "<html><font color=\"#666666\">如您的账号暂未绑定手机,或有其他疑问,请" +
                "</font><font color=\"#0000ff\">联系客服</font></html>";

        tv_html.setText(Html.fromHtml(html));

这里我们可以看到,如果你的 sdk 的版本 》= 24 的时候,这个 fromHtml(String source) 方法是显示已经废弃了的,点进去看一下情况:

这里写图片描述

很显然,我们应该使用下面的两个参数的方法,但是第二个参数 flag 是什么意思呢?
翻一下官方文档,粗略的解释一下这几个常量的含义:

Flags含义
FROM_HTML_MODE_COMPACThtml 块元素之间使用一个换行符分隔(换行)
FROM_HTML_MODE_LEGACYhtml 块元素之间使用两个换行符分隔(空一行)
FROM_HTML_OPTION_USE_CSS_COLORS应该使用CSS颜色值,而不是颜色中定义的颜色
FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE默认情况下,< blockquote>元素内的文本与其他文本使用一个换行符分隔
FROM_HTML_SEPARATOR_LINE_BREAK_DIV默认情况下,< div >元素中的文本将与其他文本使用一个换行字符分隔
FROM_HTML_SEPARATOR_LINE_BREAK_HEADING默认情况下,< h1>~< h6>元素中的文本将与其他文本使用一个换行字符分隔
FROM_HTML_SEPARATOR_LINE_BREAK_LIST默认情况下,< ul>元素中的文本将与其他文本使用一个换行字符分隔
FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM默认情况下,< li>元素中的文本将与其他文本使用一个换行字符分隔
FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH默认情况下,< p>元素中的文本将与其他文本使用一个换行字符分隔
TO_HTML_PARAGRAPH_LINES_CONSECUTIVE在< p >元素内,用“\ n”将连续的文本行分隔 (换行?)
TO_HTML_PARAGRAPH_LINES_INDIVIDUAL在< p >或< li >元素中,用“\ n”将每一行文本分隔 (换行?)


具体的效果,stackoverflow中有人给出了一个效果对比:点击查看详情

一般情况下,我们块元素之间空一行就可以了,即采用 FROM_HTML_MODE_LEGACY 这个 flag。
然后适配一下版本,最终的代码为:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_html = (TextView) findViewById(R.id.tv_html);
        String html = "<html><font color=\"#666666\">如您的账号暂未绑定手机,或有其他疑问,请" +
                "</font><font color=\"#0000ff\">联系客服</font></html>";
        tv_html.setText(fromHtml(html));

    }
    @SuppressWarnings("deprecation")
    public Spanned fromHtml(String source) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY);
        } else {
            return Html.fromHtml(source);
        }
    }

效果图太占空间了,这里就不贴了。

使用这种方式有个问题,就是文字效果出来了,怎么去处理链接,我们知道,一般的网页链接等直接使用 < ref > 标签即可,但是现在我们要点击链接实现拨打电话或者是其他行为,比如弹窗、跳转到另一个页面,这个时候好像 < ref> 就无能无力了(我没找到,有实现方法的同学欢迎留言)。所以,这里我们就用到了另外的一个方法: SpannableString

使用SpannableString

SpannableString 可以用来显示复合文本,我们可以通过 SpannableString 给文本设置各种各样的样式
比如上面的效果在不涉及点击效果,只显示不同字体颜色时,我们可以这么写:

public void setSpannableText() {
        tv_spannable = (TextView) findViewById(R.id.tv_spannable);
        String tips = "温馨提示:如果您的账号暂未绑定手机,或已忘记账号等疑问,\n 请联系客服10086";
        SpannableString spannableString=new SpannableString(tips);
        spannableString.setSpan(new ForegroundColorSpan(Color.BLUE),tips.length()-5,tips.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv_spannable.setText(tips);
    }

首先实例化 SpannableString 对象,传参就是要显示的文本,然后通过 setSpan 方法设置文本风格,最后设置 Textview。
其中 setSpan (Object what, int start, int end, int flags) 方法有四个参数,下面来大致说一下各个参数的含义。

第一个 Object 类型的参数:设置样式,如前景色、背景色、下划线、删除线等。一般样式 new 一个下面的类就好了:

Object含义
BackgroundColorSpan设置文本背景颜色
ForegroundColorSpan设置文本颜色
ClickableSpan设置点击事件
StrikethroughSpan设置删除线效果
UnderlineSpan设置下划线效果
DynamicDrawableSpan设置图片
ScaleXSpan设置基于x轴的缩放
StyleSpan设置字体样式(粗体、斜体等)
SubscriptSpan设置下标
URLSpan设置超链接


第二个参数和第三个参数:我们要特殊设置的文字的位置(该段文字的 startIndex 和 endIndex)

第四个 int 类型的参数:设置一下规则(不知道是不是这样理解的),主要有以下的几种取值:

Object含义
SPAN_EXCLUSIVE_EXCLUSIVE在文本前面或后面插入新的文本时,都不会应用该样式(前面不包括,后面不包括)
SPAN_EXCLUSIVE_INCLUSIVE在文本前插入新的文本不会应用该样式,而在文本后插入新文本会应用该样式(前面不包括,后面包括)
SPAN_INCLUSIVE_EXCLUSIVE在文本前插入新的文本会应用该样式,而在文本后插入新文本不会应用该样式(前面包括,后面不包括)
SPAN_INCLUSIVE_INCLUSIVE在文本前面或后面插入新的文本时,都会应用该样式(前面包括,后面包括)


其实这四个参数的含义挺好记的,EXCLUSIVE:排除的,即不包含 ,INCLUSIVE:包含的

上面的例子因为设置颜色的文字是固定的,所以那种方式我认为都可以,这个根据需求来。
看到这,其实实现功能与用Html实现没什么区别,那它是如何实现可点击的效果的呢?
答案就是 setSpan 方法第一个参数使用表格中的 ClickableSpan 对象,代码如下:

public void setSpannableText() {
        tv_spannable = (TextView) findViewById(R.id.tv_spannable);
        String tips = "温馨提示:如果您的账号暂未绑定手机,或已忘记账号等疑问,\n 请联系客服10086";
        SpannableString spannableString=new SpannableString(tips);
        spannableString.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "onClick:10086", Toast.LENGTH_SHORT).show();
            }
        }, tips.length() - 5, tips.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv_spannable.setText(tips);
        tv_spannable.setMovementMethod(LinkMovementMethod.getInstance());//设置可点击状态
    }

先看一下效果:
这里写图片描述

好像实现了我们想要的效果,但是字体颜色红色的呀,想要蓝色怎么办?可不可以不要下划线啊?
当然可以,重载 updateDrawState() 这个方法。在这个方法里设置可点击字体的颜色,以及去除下划线的处理

所以最终的代码是这个样子的:

public void setSpannableText() {
        tv_spannable = (TextView) findViewById(R.id.tv_spannable);
        String tips = "温馨提示:如果您的账号暂未绑定手机,或已忘记账号等疑问,\n 请联系客服10086";
        SpannableString spannableString=new SpannableString(tips);
        spannableString.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "onClick:10086", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void updateDrawState(TextPaint ds) {
                super.updateDrawState(ds);
                ds.setColor(Color.BLUE);
                ds.setUnderlineText(false);
            }
        },tips.length() - 6, tips.length()-1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv_spannable.setMovementMethod(LinkMovementMethod.getInstance());//设置可点击状态
        tv_spannable.setHighlightColor(Color.TRANSPARENT); //设置点击后的颜色为透明
        tv_spannable.setText(spannableString);

    }

不知道有没有人注意到,我第二个参数和第三个参数的值改变了,主要是因为我要点击的部分正好是在文本的结尾,如果按照原来的计算,那么我点击 10086 后面的空白部分也是会响应点击事件的,因为 TextView 的布局宽度并不是到 10086 就截止了,所以这里得做一下处理。

到此,已经了解了实现这种效果的两种方法。

他们各有优缺点,Html 实现起来简单,但是点击效果不好实现,SpannableString 可以实现点击效果,但是得计算点击文字位置。

最后,再说一下,如何调 TextView 行间距:在 xml 中加入 android:lineSpacingExtra=”10dp” 这句话,里面的值自行设置。

当然,为了方便使用,我们可以做一下封装。

参考文章

SpannableString 使用详解

Android一个 TextView 设置多种颜色的 2 种高效方法

浅谈 ClickableSpan , 实现 TextView 文本某一部分文字的点击响应

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值