Android 字母导航栏

这篇博客详细介绍了如何创建一个自定义的字母导航栏View,包括绘制A到Z的字母,处理触摸事件以及设置选中状态。代码示例展示了如何在Android中实现这一功能,涉及Android视图绘制、触摸事件处理等技术。
摘要由CSDN通过智能技术生成
/**
 * @date 2021/07/28
 * @desc 字母导航栏View
 */
class LetterView @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attributeSet, defStyleAttr) {

    private val letters = ('A'..'Z').map(Char::toString)
    private val headLetters = "我的热门"
    private var choosePosition = -1

    /**
     * 头部的文字 我的热门 所占用的高度
     */
    private var headerHeight = 0f

    private var onTouchLetterListener: ((String) -> Unit)? = null

    fun setOnTouchLetterListener(listener: ((String) -> Unit)?) {
        onTouchLetterListener = listener
    }

    private val textPaint = Paint().apply {
        textSize = 18f
        isAntiAlias = true
        color = Color.BLACK
        isFakeBoldText = true
        typeface = Typeface.DEFAULT_BOLD
    }

    private val letterBackgroundPaint = Paint().apply {
        color = Color.RED
        isAntiAlias = true
    }


    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        drawHeader(canvas)
        drawLetters(canvas)
    }

    /**
     * 画 A-Z 的字母
     */
    private fun drawLetters(canvas: Canvas?) {
        val singleHeight = (height - headerHeight) / letters.size
        for ((index, letter) in letters.withIndex()) {
            // 当前绘画的字母的所在的区域
            val rectF = RectF(
                0f,
                (singleHeight * index + headerHeight).toFloat(),
                width.toFloat(),
                (singleHeight * index + singleHeight + headerHeight).toFloat()
            )
            if (index == choosePosition) {
                textPaint.color = Color.WHITE

                // 画选中的字母的圆形背景
                canvas?.drawCircle(
                    rectF.width() / 2,
                    singleHeight * index + rectF.height() / 2 + headerHeight,
                    (rectF.width() / 2).coerceAtMost(rectF.height() / 2) * 5 / 6.toFloat(),
                    letterBackgroundPaint
                )
                // 文字的矩形区域  不用真的画出来
                // canvas?.drawRect(rectF, backgroundRectPaint)
            } else {
                textPaint.color = Color.BLACK
            }

            // 画字母
            val xPos = width / 2 - textPaint.measureText(letter) / 2

            //计算baseline
            val baseline: Float = rectF.centerY() + with(textPaint.fontMetrics) {
                (bottom - top) / 2 - bottom
            }
            canvas?.drawText(letter, xPos, baseline, textPaint)
        }
    }

    /**
     * 画 我的热门 四个字
     */
    private fun drawHeader(canvas: Canvas?) {
        headerHeight = 0f
        for ((index, letter) in headLetters.withIndex()) {
            textPaint.color = Color.BLACK
            textPaint.fontMetrics.run { bottom - top }.let {
                headerHeight += if (index == headLetters.length - 1) {
                    it * 2
                } else {
                    it
                }
                canvas?.drawText(
                    letter.toString(),
                    width / 2 - textPaint.measureText(letter.toString()) / 2,
                    it * (index + 1),
                    textPaint
                )
            }
        }
    }

    override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        val listener: ((String) -> Unit)? = onTouchLetterListener
        val oldChoose = choosePosition
        // 判断手指所在的位置 index
        val index = ((event.y - headerHeight) / ((height - headerHeight) / letters.size)).toInt()

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                if (index >= 0 && index < letters.size) {
                    listener?.invoke(letters[index])
                    choosePosition = index
                    invalidate()
                }
            }
            MotionEvent.ACTION_MOVE -> {
                if (oldChoose != index && listener != null && index >= 0 && index < letters.size) {
                    listener.invoke(letters[index])
                    choosePosition = index
                    invalidate()
                }
            }
        }
        return true
    }


    /**
     * 标记字母为选中状态
     */
    fun enableLetter(letter: String) {
        if (!letters.contains(letter)) {
            throw IllegalArgumentException("字母错误......")
        }
        choosePosition = letters.indexOf(letter)
        invalidate()
    }

    fun enableLetter(index: Int) {
        if (index !in 0..25) {
            throw IllegalArgumentException("字母下标错误......")
        }
        choosePosition = index
        invalidate()
    }


在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值