为啥要写
主要最近有中划线TextView的需求,效果如下图:

历程1
乍一看,也就那么回事,因为android的TextView本身就可以支持中划线、下划线等: 具体方法为:
textview.getPaint().setFlags(Paint. STRIKE_THRU_TEXT_FLAG);
然后通过多个TextView的拼接达到效果。这是一闪而过的解决方案,也是最笨耦合最大的方法。
存在问题:
1.无故渲染多个Widget,从性能上不可取;
2.代码冗余;
3.拓展性差,很可能后面有支持其他样式的需求。
历程2
那就只能自定义了,继承TextView实现,利用画布、Paint绘制横线,需要 2 个信息:
- 启始划线位置;
- 结束划线位置
而计算这两个信息又需要 2 个信息:
- fullText,即显示的全部Text内容
- targetText,要划横线的文本
这样的话,我们需要网自定义TextView传递这两个参数,但是存在问题:
-
破坏了统一控件的设置接口;
-
假如在画横线的基础上有额外的UI需求,如:变大,则传参变得复杂。
历程3
这时第一时间让我想到的是 SpannableString, 平时开发中经常会用来各种富文本需求,如:一段文字中间某几个字颜色/大小/点击 以及简单的图文混排。
但是呢,又不太一样,SpannableString的粒度是字符,但是我们可以利用SpannableString的对象结构来达到兼容效果。 首先来看一段设置字体大小的代码:
spannable.setSpan(object, position[0], position[1], Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
object是样式对象,可以是ForegroundColorSpan也可以是AbsoluteSizeSpan等,理论上可以是任意的对象,甚至是 Object,无论这个对象有没有意义。他的一个作用是用来设置样式,另一个作用是用来获取position[0]和position[1]的值.

的缺,可以看到,可以通过object获取start和end值。
由此我们自定义TextView如下:
public class CrossLineTextView extends AppCompatTextView{}
onDraw()方法:
//计算start和end的值
computeLineOffset();
//绘制中划线
drawCrossLine(canvas);
computeLineOffset()方法部分代码
SpannedString ss = (SpannedString) getText();
Paint paint = getPaint();
//下划线启始字符位置
int startIndex = ss.getSpanStart(this);
//下划线结束字符位置
int endIndex = ss.getSpanEnd(this);
我们还需要把启始位置转换成offset像素单位。
String content = getText().toString();
if(TextUtils.isEmpty(content)){
return;
}
if(startIndex < 0 || endIndex <= startIndex || endIndex >= content.length()){
return;
}
String prefix;
if(startIndex == 0){
prefix = "";
}else{
prefix = content.substring(0, startIndex);
}
String suffix = content.substring(startIndex, endIndex + 1);
//别忘记加上padding
start = paint.measureText(prefix) + getPaddingLeft();
end = start + paint.measureText(suffix);
drawCrossLine()方法
private void drawCrossLine(@NonNull Canvas canvas){
if(start != -1 && end != -1 && end > start){
if(linePaint == null){
linePaint = new Paint();
linePaint.setColor(getPaint().getColor());
linePaint.setStrokeWidth(lineWidth);
}
canvas.drawLine(start, getMeasuredHeight() * HEIGHT_PERCENT,
end, getMeasuredHeight() * HEIGHT_PERCENT, linePaint);
}
}
这就是核心(垃圾)代码。具体使用方法是:
SpannableString crossSpanString = new SpannableString("我爱我的祖国,也爱那个祖国");
crossSpanString.setSpan(tipTv, 4, 8, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
tipTv.setText(crossSpanString);
就酱紫,我们甚至可以在crossSpanString上继续叠加各种其他效果,这样就可以不破坏原有统一控件的接口,不影响原有功能和使用。
总结
记录本文仅仅是分享今天工作的一点小想法和大家探讨,又或是闲来无事,希望能抛砖引玉。仅此而已!