最近项目中有一个小小的细节,就是一般温馨提示语,一段文字,但是中间会包含一些字体可点击,有特殊颜色,比如下图的温馨提示:
一般实现方式有以下几种:
1.TextView 拼接
2.加载 Html
3.使用 SpannableString
其中的第一种方法,这里就不考虑了,因为这里也不确定字体到底占几行,并且链接文字位置也不可控
那下面就来看一下后面两种的实现方式。
这里主要就是用 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_COMPACT | html 块元素之间使用一个换行符分隔(换行) |
FROM_HTML_MODE_LEGACY | html 块元素之间使用两个换行符分隔(空一行) |
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 = "如您的账号暂未绑定手机,或有其他疑问,请" +
"联系客服";
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 给文本设置各种各样的样式
比如上面的效果在不涉及点击效果,只显示不同字体颜色时,我们可以这么写:
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 文本某一部分文字的点击响应