目标
平时要实现如下的效果,需要用Spannablestring。
但是代码写的老长了,还要算坐标,还需要记住各种效果对应的CharacterStyle子类,代码如下:
SpannableString spanText = new SpannableString("特殊效果的字符串");
SpanspanText.setSpan(new TypefaceSpan("monospace"), 3, 10,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
SpanspanText.setSpan(CharacterStyle子类(),下标,下标,区间方式)
mTVText.append(spanText);
mTVText.setMovementMethod(new LinkMovementMethod());
咱们既然用kotlin写,肯定希望用它的语法糖把这段代码优雅的实现,咱们实现的效果如下,当然可能还有更简洁的实现方式。
setText("前景色".style { foregroundColor(Color.RED) }
+ "背景色".style { backgroundColor(Color.BLUE) }
+ "删除线".style { strikethrough() }
+ "下划线".style { underline() }
+ "设置图片".style { dynamicDrawable(resources.getDrawable(R.drawable.icon_address),true) }
+ "大号的字体".style { absoluteSize(DimensAdapter.textPxSize(CustomTSDimens.BIG).toInt()) }
+ "2倍的字体".style { relativeSize(2f) }
+ "粗体".style { style(Typeface.BOLD) }
+ "x抽缩放3倍".style { scaleX(3f) }
+ "图片图片".style { image(resources.getDrawable(R.drawable.icon_user)) }
+ "我是下标".style { subscript() }
+ "我是上标".style { superscript() }
+ "http://www.baidu.com".style { url("http://www.baidu.com") })
实现方式
定义一个实体类(TextSpan)记录样式的信息(直接贴代码)
class TextSpan(val content: String) {
//所有样式
val styles = mutableListOf(mutableListOf())
val textConstructor = mutableListOf(content)
// var startIndex = 0
// private set(value) {
// field = value
// endIndex = value + content.length
// }
// var endIndex = 0
// private set
fun backgroundColor(colorVal: Int) {
styles[0].add(BackgroundColorSpan(colorVal))
}
fun foregroundColor(colorVal: Int) {
styles[0].add(ForegroundColorSpan(colorVal))
}
/**
* 模糊(BlurMaskFilter)、浮雕(EmbossMaskFilter) BlurMaskFilter
*/
fun maskFilter(maskFilter: MaskFilter) {
styles[0].add(MaskFilterSpan(maskFilter))
}
/**
* 光栅效果 StrikethroughSpan()
*/
fun rasterizer(rasterizer: Rasterizer) {
styles[0].add(RasterizerSpan(rasterizer))
}
/**
* 删除线(中划线)
*/
fun strikethrough() {
styles[0].add(StrikethroughSpan())
}
/** * 下划线 */
fun underline() {
styles[0].add(UnderlineSpan())
}
/**
* 设置图片 (DynamicDrawableSpan.ALIGN_BASELINE or DynamicDrawableSpan.ALIGN_BOTTOM)
*/
fun dynamicDrawable(drawable: Drawable, isAlignBaseLine: Boolean) {
styles[0].add(object : DynamicDrawableSpan(if (isAlignBaseLine) DynamicDrawableSpan.ALIGN_BASELINE else DynamicDrawableSpan.ALIGN_BOTTOM) {
override fun getDrawable(): Drawable {
drawable.setBounds(0, 0, drawable.minimumWidth, drawable.minimumHeight)
return drawable
}
})
}
/**
* 字体大小(像素)
*/
fun absoluteSize(textSize: Int) {
styles[0].add(AbsoluteSizeSpan(textSize, false))
}
/** * 图片 */
fun image(drawable: Drawable, width: Int = drawable.minimumWidth, height: Int = drawable.minimumHeight) {
drawable.setBounds(0, 0, width, height)
styles[0].add(ImageSpan(drawable))
}
/**
* ScaleXSpan 基于x轴缩放
*/
fun scaleX(scaleRate: Float) {
styles[0].add(ScaleXSpan(scaleRate))
}
/**
* 相对大小(文本字体)
*/
fun relativeSize(scanRate: Float) {
styles[0].add(RelativeSizeSpan(scanRate))
}
/**
* 字体样式:粗体、斜体等 Typeface
*/
fun style(typeface: Int) {
styles[0].add(StyleSpan(typeface))
}
/** * 下标(数学公式会用到) */
fun subscript() {
styles[0].add(SubscriptSpan())
}
/** * 上标(数学公式会用到) */
fun superscript() {
styles[0].add(SuperscriptSpan())
}
/** * 文本字体 */
fun typeface(typeface: String) {
styles[0].add(TypefaceSpan(typeface))
}
/** * 文本超链接 */
fun url(linkAddress: String) {
styles[0].add(URLSpan(linkAddress))
}
}
实现+的算法,用到运算符重载
operator fun plus(nextVal: TextSpan): TextSpan {
styles.addAll(nextVal.styles)
textConstructor.addAll(nextVal.textConstructor)
return this
}
最后TextView.setText(TextSpan) 可以用,需要给TextView扩展一个方法
(ps:我这里面是2个循环不是很好,可能还有更好的办法去做)
fun TextView.setText(textSpan: TextSpan) {
val builder = StringBuilder()
textSpan.textConstructor.forEach{
builder.append(it)
}
val spanStr = SpannableString(builder.toString())
var index = 0
textSpan.textConstructor.forEachIndexed { position, str ->
val end = index + str.length
textSpan.styles[position].forEach {
spanStr.setSpan(it, index, end,Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
if(it is URLSpan) this.movementMethod = LinkMovementMethod()
}
index+= str.length
}
text = spanStr
}
这样就大功告成了.