开会,工作,未学习。
自定义控件 SideBar,自定义了几个属性,即文字大小 textSize,文字颜色和选中后的颜色,还有文件间的垂直边距 textVerticalMargin。
class SideBar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : View(context, attrs, defStyleAttr, defStyleRes) {
private val paint: Paint = Paint().apply { textAlign = Paint.Align.CENTER }
var textSize = 0f
var textColor = 0
var textColorSelected = 0
var textVerticalMargin = 0f
init {
val attributes = context.obtainStyledAttributes(attrs, R.styleable.SideBar)
textSize = attributes.getDimension(R.styleable.SideBar_textSize, 20f)
textColor = attributes.getColor(R.styleable.SideBar_textColor, Color.parseColor("#1B6EFD"))
textColorSelected = attributes.getColor(R.styleable.SideBar_textColorSelected, Color.parseColor("#1B6EFD"))
textVerticalMargin = attributes.getDimension(R.styleable.SideBar_textVerticalMargin, 4f)
attributes.recycle()
}
}
当然绘制文字,需要知道高度。paint 设置水平居中,垂直位置文字以 baseline 为坐标,所以选出文字的高度 textHeight,减去它下面的 descent,就是文字 baseline 的位置。
private var textHeight = 0f
private var fontDescent = 0f
private fun getFontMetrics(textSize: Float) : Paint.FontMetrics {
paint.textSize = textSize
val fontMetrics = Paint.FontMetrics()
paint.getFontMetrics(fontMetrics)
return fontMetrics
}
var textSize = 0f
set(value) {
field = value
paint.textSize = value
val metrics = getFontMetrics(value)
textHeight = abs(metrics.descent - metrics.ascent)
fontDescent = abs(metrics.descent)
}
在设置 textSize 大小后,立即重新计算文字的高度 textHeight 和下面的 descent 值。
外部设置数据源 datas 后,重新绘制。
var datas: Collection<String>? = null
set(value) {
field = value
invalidate()
}
首先要绘制文字,我是先计算控件的高度 measuredHeight 减去所有文字的高度加上中间 margin 的大小,如果大于 0,说明有空间绘制。
private fun getSpaceHeight() = if (datas.isNullOrEmpty()) measuredHeight.toFloat()
else
measuredHeight - datas!!.size * textHeight -
(datas!!.size - 1) * textVerticalMargin
然后看 onDraw 方法。
var textSelectedIndex = -1
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
if (datas.isNullOrEmpty()) return
if (getSpaceHeight() < 0) {
Toast.makeText(context, "SideBar分配的高度不够展示", Toast.LENGTH_SHORT).show()
return
}
// 因为下面为了统一计算,所以先减掉一个 margin
var lastY = getSpaceHeight()/2f - textVerticalMargin;
datas?.forEachIndexed { index, data ->
if (index == textSelectedIndex) {
paint.color = textColorSelected
} else {
paint.color = textColor
}
// lastY 用于循环中确定文字最底部的坐标
lastY += textVerticalMargin + textHeight
canvas?.drawText(data, measuredWidth/2f, lastY - fontDescent, paint)
}
}
网友评论