背景
最近在开发的一个项目中,有些字体中用到了描边效果,于是在网上找了几个字体描边效果的自定义View用,但是都有点小问题,于是自己写了个自定义View。
效果图及问题
1.字体2,在播放motionlayout动画时字体描边出现了错位 2.字体3,边缘部分被裁剪
自定义View绘制
1.确定View的大小
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//测量字体所在矩形(mRect)的大小
mPaint.getTextBounds(mTextStr, 0, mTextStr.length, mRect)
//设置自定义View的宽高,即mRect的宽高,加上描边的大小(mStrokeSize)是为了描边不被裁掉
setMeasuredDimension(mRect.width()+mStrokeSize, mRect.height()+mStrokeSize*2)
}
2.确定字体中心线的位置
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
val fontMetrics = mPaint.fontMetrics
baseLine =(fontMetrics.bottom-fontMetrics.top)/2-fontMetrics.bottom
}
3.绘制描边字体
描边其实就是绘制两个相同的字体,所用画笔设置不同描边大小就能实现
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.apply {
mPaint.color = mStrokeColor
mPaint.strokeWidth = mStrokeSize.toFloat()
drawText(mTextStr,width/2f,height/2f+baseLine,mPaint)
mPaint.color = mTextColor
mPaint.strokeWidth = 0f
drawText(mTextStr,width/2f,height/2f+baseLine,mPaint)
}
}
代码
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import com.wjm.base.SizeUtil
import com.wjm.need.R
class StrokeView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val mPaint =Paint()
private val mRect = Rect()
private var baseLine = 0f
private var mTextStr= "666"
private var mStrokeSize = 0
private var mTextSize = 0
private var mStrokeColor = Color.BLACK
private var mTextColor = Color.WHITE
init {
val ob = context.obtainStyledAttributes(attrs, R.styleable.StrokeView)
ob.getString(R.styleable.StrokeView_w_stroke_text)?.let {
mTextStr=it
}
mStrokeColor=ob.getColor(R.styleable.StrokeView_w_stroke_color,Color.BLACK)
mTextColor=ob.getColor(R.styleable.StrokeView_w_stroke_textColor,Color.WHITE)
mStrokeSize = ob.getDimensionPixelSize(R.styleable.StrokeView_w_stroke_size, 2)
mTextSize = ob.getDimensionPixelSize(R.styleable.StrokeView_w_stroke_textSize, 14)
ob.recycle()
mPaint.apply {
style = Paint.Style.FILL_AND_STROKE
isAntiAlias=true
textAlign=Paint.Align.CENTER
strokeJoin=Paint.Join.ROUND
textSize = mTextSize.toFloat()
isDither =true
}
}
fun setTextContent(str:String){
mTextStr = str
invalidate()
}
fun setTextSize(value:Float){
mPaint.textSize = SizeUtil.sp2px(value).toFloat()
invalidate()
}
fun setTextColor(color:Int){
mTextColor = color
invalidate()
}
fun setTextStrokeColor(color: Int){
mStrokeColor = color
invalidate()
}
fun setTextStrokeWidth(value: Float){
mStrokeSize = SizeUtil.dp2px(value)
invalidate()
}
//设置特殊字体需要外部传入
fun setTextFontType(typeface: Typeface){
mPaint.typeface = typeface
invalidate()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//测量字体所在矩形(mRect)的宽度
mPaint.getTextBounds(mTextStr, 0, mTextStr.length, mRect)
//设置自定义View的宽高,即mRect的宽高,加上描边的大小(mStrokeSize)是为了描边不被裁掉
setMeasuredDimension(mRect.width()+mStrokeSize, mRect.height()+mStrokeSize*2)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
val fontMetrics = mPaint.fontMetrics
baseLine =(fontMetrics.bottom-fontMetrics.top)/2-fontMetrics.bottom
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.apply {
mPaint.color = mStrokeColor
mPaint.strokeWidth = mStrokeSize.toFloat()
drawText(mTextStr,width/2f,height/2f+baseLine,mPaint)
mPaint.color = mTextColor
mPaint.strokeWidth = 0f
drawText(mTextStr,width/2f,height/2f+baseLine,mPaint)
}
}
}
自定义属性
<declare-styleable name="StrokeView">
<attr name="w_stroke_text" format="string"/>
<attr name="w_stroke_color" format="color"/>
<attr name="w_stroke_size" format="dimension"/>
<attr name="w_stroke_textSize" format="dimension"/>
<attr name="w_stroke_textColor" format="color"/>
</declare-styleable>