前言
在开发过程中,TextView中会出现一些特殊内容(如:部分内容颜色、字体、大小不同,并且部分字体可点击),写多个TextView又会显得麻烦,那又怎么解决这个问题呢?首页我们来看看TextView的源码,在android.text.style包下,有很多Span类,那么我们可以使用SpannableStringBuilder来设置Span,先看看实现效果:
使用方式:
val builder = SpannableStringUtil.create(context!!)
.setText("类似于HTML中的<li>标签的圆点效果")
.setBullet(android.R.color.holo_green_dark)//添加圆点
.setTextColor(android.R.color.black)
.setText("\n设置文字左侧显示引用样式")
.setQuote(android.R.color.holo_green_dark)
.setText("\n设置下划线以及删除线")
.setTextUnderline()//下划线
.setTextStrikeThrough()//删除线
.setTextColor(android.R.color.holo_red_dark)
.setTextSize(20)
.setTextStyle(Typeface.ITALIC)
.setText("这个可以点击")
.setTextColor(android.R.color.holo_green_dark)
.setTextSize(21)
.setTextStyle(Typeface.BOLD)//字体样式
.setClick(tv_span1,{
Log.e("data", it)
},true)//设置点击
.setText("这里设置下标")
.setTextColor(android.R.color.holo_blue_dark)
.setTextSubscript(6)//下标
.setTextColor(android.R.color.holo_orange_dark)
.setText("这里可以设置上标")
.setScaleX(1.5f)
.setTextColor(android.R.color.holo_green_dark)
.setTextSuperscript(6)//上标
.setTextColor(android.R.color.holo_blue_dark)
.setDrawable(R.mipmap.ic_launcher,100,100)
.setAlign(Layout.Alignment.ALIGN_OPPOSITE)
.build()
tv_span1.text = builder
val builder1 = SpannableStringUtil.create(context!!)
.setText("人的一生,有许多事情,是需要放在心里慢慢回味的,过去的就莫要追悔,一切向前看吧 任何打击都不足以成为你堕落的借口,即使你改变不了这个世界,你却依然可以改变自己,选择条正确的路永远走下去。")
.setTextColor(android.R.color.holo_orange_dark)
.setTextSize(17)
.setTextLeadingMargin(30)//设置文本缩进
.build()
tv_span2.text = builder1
SpannableStringUtil
class SpannableStringUtil {
private var builder = SpannableStringBuilder()
private val infoList = mutableListOf<StringInfo>()
companion object {
private lateinit var context: Context
fun create(mContext: Context): SpannableStringUtil{
context=mContext
return SpannableStringUtil()
}
}
/**
* 设置文字
*/
fun setText(text:String):SpannableStringUtil{
builder.append(text)
infoList.add(StringInfo())
val lastInfo=getLastInfo()
lastInfo.startIndex=builder.length- text.length
lastInfo.endIndex=builder.length
return this
}
/**
* 设置文字颜色
*/
fun setTextColor(color:Int):SpannableStringUtil{
val span= ForegroundColorSpan(ContextCompat.getColor(context,color))
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文字背景颜色
*/
fun setTextBgColor(color:Int):SpannableStringUtil{
val span= BackgroundColorSpan(ContextCompat.getColor(context,color))
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文字大小
*/
fun setTextSize(size:Int):SpannableStringUtil{
//true为size的单位是dip,false为px
val span= AbsoluteSizeSpan(size,true)
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文字删除线
*/
fun setTextStrikeThrough():SpannableStringUtil{
val span= StrikethroughSpan()
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文字下划线
*/
fun setTextUnderline():SpannableStringUtil{
val span= UnderlineSpan()
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文本缩进
* @param first 首行的 margin left 偏移量。
* @param rest 其他行的 margin left 偏移量。
*/
fun setTextLeadingMargin(first:Int,rest:Int=0):SpannableStringUtil{
val span= LeadingMarginSpan.Standard(dip2px(first.toFloat()), dip2px(rest.toFloat()))
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文字下标,化学公式中用到
*/
fun setTextSubscript(number:Int):SpannableStringUtil{
val parcel = Parcel.obtain()
parcel.writeInt(number)
val span= SubscriptSpan(parcel)
builder.append(number.toString())
infoList.add(StringInfo())
val stringInfo = getLastInfo()
stringInfo.startIndex=builder.length- number.toString().length
stringInfo.endIndex=builder.length
stringInfo.styleList.add(span)
parcel.recycle()
return this
}
/**
* 设置文字上标,数学公式中用到
*/
fun setTextSuperscript(number:Int):SpannableStringUtil{
val parcel = Parcel.obtain()
parcel.writeInt(number)
val span= SuperscriptSpan(parcel)
builder.append(number.toString())
infoList.add(StringInfo())
val stringInfo = getLastInfo()
stringInfo.startIndex=builder.length- number.toString().length
stringInfo.endIndex=builder.length
stringInfo.styleList.add(span)
parcel.recycle()
return this
}
/**
* 设置文字横向缩放比例
* @param proportion 缩放比例
*/
fun setScaleX(proportion:Float):SpannableStringUtil{
val span= ScaleXSpan(proportion)
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文字相对大小
* @param proportion 文字大小比例
*/
fun setTextRelativeSize(proportion:Float):SpannableStringUtil{
val span= RelativeSizeSpan(proportion)
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文字对齐方式
*/
fun setAlign(align:Layout.Alignment):SpannableStringUtil{
val span= AlignmentSpan.Standard(align)
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置文字左侧显示引用样式(一条竖线)
* @param color 竖线颜色
* @param stripeWidth 竖线宽度
* @param gapWidth 文字与竖线距离
*/
@SuppressLint("NewApi")
fun setQuote(color:Int,stripeWidth:Int=5,gapWidth:Int=20):SpannableStringUtil{
val span= QuoteSpan(ContextCompat.getColor(context,color),stripeWidth,gapWidth)
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 类似于HTML中的<li>标签的圆点效果
* @param gapWidth 圆点与文本的间距
* @param color 圆点颜色
* @param gapRadius 圆点半径
*/
@SuppressLint("NewApi")
fun setBullet(color:Int,gapWidth:Int=10,gapRadius:Int=10):SpannableStringUtil{
val span= BulletSpan(gapWidth, ContextCompat.getColor(context,color),gapRadius)
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 模糊效果
* @param radius 模糊半径(需大于0)
* @param style 模糊样式
*/
fun setBlur(radius:Float,style:BlurMaskFilter.Blur):SpannableStringUtil{
val span= MaskFilterSpan(BlurMaskFilter(radius,style))
val stringInfo = getLastInfo()
stringInfo.styleList.add(span)
return this
}
/**
* 设置图片
* @param drawableWidth 宽
* @param drawableHeight 高
*/
fun setDrawable(drawableId:Int,drawableWidth:Int,drawableHeight:Int):SpannableStringUtil{
val drawable = ContextCompat.getDrawable(context,drawableId)!!
drawable.setBounds(0, 0, drawableWidth, drawableHeight)
val span=ImageSpan(drawable)
builder.append("0")
infoList.add(StringInfo())
val lastInfo = getLastInfo()
lastInfo.startIndex=builder.length -1
lastInfo.endIndex=builder.length
lastInfo.styleList.add(span)
return this
}
/**
* 设置点击事件
* @param isUnderline 是否去掉下划线,不传值默认去掉
*/
fun setClick(textView:TextView, listener: (clickStr:String) -> Unit,isUnderline:Boolean=false):SpannableStringUtil{
textView.movementMethod = LinkMovementMethod.getInstance()
textView.highlightColor = 0x00000000
val lastInfo = getLastInfo()
val span=object : ClickableSpan() {
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
if (!isUnderline) {
// 去除下划线
ds.isUnderlineText = false
ds.bgColor = Color.TRANSPARENT
}
}
override fun onClick(view: View) {
listener.invoke(builder.subSequence(lastInfo.startIndex,lastInfo.endIndex).toString())
}
}
lastInfo.styleList.add(span)
return this
}
/**
* 设置文字样式
* @param style
* Typeface.NORMAL 正常
* Typeface.BOLD 粗体
* Typeface.ITALIC 斜体
* Typeface.BOLD_ITALIC 粗体+斜体
*/
fun setTextStyle(style:Int):SpannableStringUtil{
val stringInfo = getLastInfo()
val span = StyleSpan(style)
stringInfo.styleList.add(span)
return this
}
fun build(): SpannableStringBuilder {
infoList.forEach {
if (it.styleList.size > 0) {
for (i in 0 until it.styleList.size) {
val style = it.styleList[i]
builder.setSpan(
style,
it.startIndex,
it.endIndex,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE
)
}
}
}
return builder
}
private fun getLastInfo(): StringInfo {
return infoList[infoList.size - 1]
}
private class StringInfo{
var startIndex=0
var endIndex=0
var styleList= mutableListOf<Any>()
}
private fun dip2px(dpValue: Float):Int{
return (dpValue * context.resources.displayMetrics.density + 0.5f).toInt()
}
}