背景
最近项目上要实现一个功能(红色选中部分),先看图:
分析
大家仔细看红色选中部分,会发现文字内容 大小不一样、颜色也不一样,数字199
还带有一条删除线,那要怎么实现这种效果呢?
实现方法一
¥189
为一个TextView,次/人
为一个TextView,- 原价¥
一个TextView,199
一个TextView,四个TextView横排在一起就可以达到我们想要的效果。
实现方法二
只用一个TextView来实现,把文本内容分成4块,分别为
¥189
、次/人
、- 原价¥
、199
。
然后用SpannableStringBuilder
的append方法把4块拼接起来即可。
对比两种实现方法,我们肯定是选择第二种了,那开始具体实现之前我们先来普及下SpannableStringBuilder
与SpannableString
的基本知识。
SpannableString与SpannableStringBuilder使用
-
SpannableString
、SpannableStringBuilder
与String
的关系
首先SpannableString
、SpannableStringBuilder
基本上与String
差不多,也是用来存储字符串,但它们俩的特殊就在于有一个SetSpan
函数,能给这些存储的String
添加各种格式或者称样式(Span)
,将原来的String
以不同的样式显示出来,比如在原来String
上加下划线、加背景色、改变字体颜色、用图片把指定的文字给替换掉,等等。
所以,总而言之,SpannableString
、SpannableStringBuilder
与String
一样, 首先也是传字符串,
但SpannableString
、SpannableStringBuilder
可以对这些字符串添加额外的样式信息,而String
则不行。
注意:如果这些额外信息能被所用的方式支持,比如将
SpannableString
传给TextView
;
也有对这些额外信息不支持的,比如Canvas
绘制文字,对于不支持的情况,SpannableString
和SpannableStringBuilder
就是退化为String
类型,直接显示原来的String
字符串,而不会再显示这些附加的额外信息,即添加的样式无效果。
-
SpannableString
与SpannableStringBuilder
区别
它们的区别在于 SpannableString
像一个String
一样,构造对象的时候传入一个String
,之后再无法更改String的内容,也无法拼接多个 SpannableString
;而SpannableStringBuilder
则更像是StringBuilder
,它可以通过其append()
方法来拼接多个String
;
-
setSpan
方法
void setSpan (Object what, int start, int end, int flags)
函数意义:给SpannableString
或SpannableStringBuilder
特定范围的字符串设定Span
样式,可以设置多个(比如同时加上下划线和删除线等),flags
参数标识了当在所标记范围前和标记范围后紧贴着插入新字符时的动作,即是否对新插入的字符应用同样的样式。
参数说明:
object what
:对应的各种Span
,后面会提到;
int start
:开始应用指定Span
的位置,索引从0开始
int end
:结束应用指定Span
的位置,特效并不包括这个位置。比如如果这里数为3(即第4个字符),第4个字符不会有任何特效。从下面的例子也可以看出来。
int flags
:取值有如下四个
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
:不包括start
、end
位置,即start
、end
位置不会应用设置的样式。
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
:不包括start
位置,包括end
位置。即start
位置不会应用设置的样式,end
位置应用设置的样式。
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
:包括start
位置,不包括end
位置。即start
位置会应用设置的样式,end
位置不会应用设置的样式。
Spannable.SPAN_INCLUSIVE_INCLUSIVE :包括start
、end
位置,即start
、end
位置都会应用设置的样式。
具体实现
因为实现起来没那么复杂,主要就是调用setSpan
创建各种样式span
,直接上代码,代码中有关键注释:
//中等字体
int middleFontSize = (int) DensityHelper.spToPx(context, 18);
//小字体
int smallFontSize = (int) DensityHelper.spToPx(context, 14);
//红色 #F65236
int colorRed = ContextCompat.getColor(context, R.color.couponColor);
//灰色 #999999
int textColorThird = ContextCompat.getColor(context, R.color.textColorThird);
SpannableStringBuilder spanBuilder = new SpannableStringBuilder();
String price = "¥189";
int start = 0;
int end = price.length();
spanBuilder.append(price);
//添加字体大小样式span
spanBuilder.setSpan(new AbsoluteSizeSpan(middleFontSize), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//添加文本颜色样式span
spanBuilder.setSpan(new ForegroundColorSpan(colorRed), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
String priceUnit = "次/人";
start = end;
end += priceUnit.length();
spanBuilder.append(priceUnit);
spanBuilder.setSpan(new AbsoluteSizeSpan(smallFontSize), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spanBuilder.setSpan(new ForegroundColorSpan(colorRed), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
String line = " - 原价¥";
start = end;
end += line.length();
spanBuilder.append(line);
spanBuilder.setSpan(new AbsoluteSizeSpan(smallFontSize), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spanBuilder.setSpan(new ForegroundColorSpan(textColorThird), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
String originalPricePrefix = "199";
start = end;
end += originalPricePrefix.length();
spanBuilder.append(originalPricePrefix);
spanBuilder.setSpan(new AbsoluteSizeSpan(smallFontSize), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spanBuilder.setSpan(new ForegroundColorSpan(textColorThird), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//添加删除线样式span
spanBuilder.setSpan(new StrikethroughSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//最终把SpannableStringBuilder设置给TextView
tvPriceInfo.setText(spanBuilder);
以上由于项目需求,只介绍了如何创建字体大小span、文本颜色span、加删除线span,还有很多比如文字模糊、浮雕、文字背景颜色、文字粗体斜体、下划线、超链接等等,大家敲敲代码感受下。
最后,如果觉得对你有用的话,那就动动手指给个赞吧。有疑问请留言,一起进步哈!
附上DensityHelper
工具类的实现,主要实现dp、sp与px的互相转换。
public final class DensityHelper {
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static float dipToPx(Context context, float dp) {
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float px = dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
return px;
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static float pxToDip(Context context, float px) {
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float dp = px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
return dp;
}
/**
* 将px值转换为sp值,保证文字大小不变
*
* @param px 像素值
* @param context 上下文
* @return px转换后的sp
*/
public static float pxToSp(Context context, float px) {
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float sp = px / metrics.scaledDensity + 0.5f;
return sp;
}
/**
* 将sp值转换为px值,保证文字大小不变
*
* @param sp 字体sp大小
* @param context (上下文
* @return sp转换后的px值
*/
public static float spToPx(Context context, float sp) {
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float px = sp * metrics.scaledDensity + 0.5f;
return px;
}
}