美文网首页
2022-11-10-写控件

2022-11-10-写控件

作者: 三流之路 | 来源:发表于2022-11-11 12:32 被阅读0次

开会,工作,未学习。


自定义控件 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)  
    }  
}

相关文章

网友评论

      本文标题:2022-11-10-写控件

      本文链接:https://www.haomeiwen.com/subject/ptbztdtx.html