android iconfont带图标的图文并茂的一种实现

android实现图文并茂方法很多。
这里针对,仅本地图标,需要对齐,任意位置,兼容换行导致后面空白的问题做的一种方案。

www.iconfont.cn,注册;
上传svg的icon;
下载项目得到iconfont.ttf;
Typeface.createFromAsset得到TF。
替换unicode码iconfont的16进制,比如  改成\ue602
通过CustomTypefaceSpan, RelativeSizeSpan等修改语句中的unicode部分,拼接到ssb中设置给textView。

其他:
iconfont 某些svg上传到iconfont出现图标不对的问题。
参考:
https://zhuanlan.zhihu.com/p/494625217
第1个,先做outline stroke,轮廓化描边;
第2个,将多个图层合并成一层;
第3个,使用插件fill rule editor调整。

效果图:
在这里插入图片描述

class CustomTypefaceSpan(family: String?, private val newType: Typeface) : TypefaceSpan(family) {

    override fun updateDrawState(ds: TextPaint) {
        applyCustomTypeFace(ds, newType)
    }

    override fun updateMeasureState(paint: TextPaint) {
        applyCustomTypeFace(paint, newType)
    }

    companion object {
        private fun applyCustomTypeFace(paint: Paint, tf: Typeface) {
            val oldStyle: Int
            val old = paint.typeface
            oldStyle = old?.style ?: 0
            val fake = oldStyle and tf.style.inv()
            if (fake and Typeface.BOLD != 0) {
                paint.isFakeBoldText = true
            }
            if (fake and Typeface.ITALIC != 0) {
                paint.textSkewX = -0.25f
            }
            paint.typeface = tf
        }
    }
}

//如果有必要cache就通过hashmap来cache
fun getOrCreateFontFace(context: Context, assetsPath: String?) : Typeface? {
    if(assetsPath.isNullOrEmpty()) return null
    return Typeface.createFromAsset(context.assets, assetsPath)
}

/**
 * 一部分一部分的拼接
 */
interface IIconFontPart

/**
 * 拼接文字
 */
data class IconFontNormalPart(val normalText:String) : IIconFontPart

/**
 * 拼接上一个unicode的iconFont
 */
open class IconFontIconPart(val unicode:Char, val colorStr:String? = null, val relativeSize:Float? = null) : IIconFontPart

/**
 * 要求TextView自身已经具有常规的字体;常规的textSize;常规的颜色。
 *
 * 再来设置结合iconFontPart。
 */
fun TextView.setIconFont(vararg parts:IIconFontPart) {
    val sb = StringBuilder()
    parts.forEach {
        if (it is IconFontNormalPart) {
            sb.append(it.normalText)
        } else if (it is IconFontIconPart) {
            sb.append(it.unicode)
        }
    }
    val text = sb.toString()
    val ss = SpannableStringBuilder(text)

    var len = 0
    val iconTf = getOrCreateFontFace(globalContext, "fonts/iconfont.ttf")!!
    parts.forEach {
        if (it is IconFontNormalPart) {
            len += it.normalText.length
        } else if (it is IconFontIconPart) {
            ss.setSpan(CustomTypefaceSpan("", iconTf), len, len + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
            if (it.colorStr != null) {
                ss.setSpan(ForegroundColorSpan(Color.parseColor(it.colorStr)), len, len + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
            }
            if (it.relativeSize != null) {
                ss.setSpan(RelativeSizeSpan(it.relativeSize), len, len + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
            }
            len += 1 //最后
        }
    }
    setText(ss)
}

那么使用上就会很简单了。

val blank = IconFontNormalPart(" ")
 binding.textView.setIconFont(
     IconFontNormalPart("小明和小红 "),
     IconFontIconPart('\ue602'),
     blank,
     IconFontIconPart('\ue604'),
     blank,
     IconFontIconPart('\ue604', relativeSize = 1.25f),
     IconFontNormalPart("一起去打水 "),
     IconFontIconPart('\ue604', colorStr = "#ff8899"),)

自动换行右侧空白问题

英文的自动换行可能会导致文字显示过长,而自动换行后右侧空出太多,如下图:
请添加图片描述
解决方案给基础TextView继承后添加代码:

//解决换行右侧太多的问题
var fixWrapEndSpace = false

//https://stackoverflow.com/questions/50287198/textview-remove-space-after-line-break
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    if (fixWrapEndSpace) {
        var maxWidth = ceil(getMaxLineWidth(layout)).toInt()
        maxWidth += paddingRight + paddingLeft
        setMeasuredDimension(maxWidth, measuredHeight)
    }
}

private fun getMaxLineWidth(layout: Layout): Float {
    var maximumWidth = 0.0f
    val lines = layout.lineCount
    for (i in 0 until lines) {
        maximumWidth = max(layout.getLineWidth(i), maximumWidth)
    }

    return maximumWidth
}
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值