Android开发之 仿微信通讯录 (二)侧边首字母导航控件


import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.support.annotation.ColorInt
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.xiaolei.library.Exts.dp2px
import com.xiaolei.library.Exts.measureHeight
import com.xiaolei.library.Exts.measureWidth
import com.xiaolei.library.Exts.sp2px
import java.util.*

class LatterNaviBar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr)
{
    private var mHeight = 0
    private var mWidth = 0
    private var wordRect = Rect()
    private val paint = Paint()
    private val words = LinkedList<Word>()

    private val worldSpace by lazy { dp2px(11) }
    private var selectListener: ((word: String) -> Unit)? = null
    private var downListener: (() -> Unit)? = null
    private var upListener: (() -> Unit)? = null

    init
    {
        paint.color = Color.parseColor("#40e0d6")
        paint.textSize = sp2px(10).toFloat()
        paint.isAntiAlias = true
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
    {
        mHeight = measureHeight(heightMeasureSpec)
        mWidth = measureWidth(widthMeasureSpec)
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    /**
     * 设置文字颜色
     */
    fun setTextColor(@ColorInt color: Int)
    {
        paint.color = color
        invalidate()
    }

    /**
     * 设置文字大小,单位 sp
     */
    fun setTextSize(sp: Int)
    {
        paint.textSize = sp2px(sp).toFloat()
        invalidate()
    }

    /**
     * 设置要显示的文字
     */
    fun setWords(words: Array<String>)
    {
        this.words.clear()
        val tmp = words.map { Word(it, Rect()) }
        this.words.addAll(tmp)
        invalidate()
    }

    /**
     * 设置选择监听
     */
    fun setOnSelect(listener: (word: String) -> Unit)
    {
        selectListener = listener
    }

    /**
     * 设置按下去的监听
     */
    fun setOnDown(listener: () -> Unit)
    {
        downListener = listener
    }

    /**
     * 设置弹起的监听
     */
    fun setOnUp(listener: () -> Unit)
    {
        upListener = listener
    }

    override fun onTouchEvent(event: MotionEvent): Boolean
    {
        val y = event.y
        when (event.action)
        {
            MotionEvent.ACTION_DOWN,
            MotionEvent.ACTION_MOVE ->
            {
                if (event.action == MotionEvent.ACTION_DOWN)
                {
                    downListener?.invoke()
                }
                for (w in words)
                {
                    if (w.rect.top <= y && w.rect.bottom >= y)
                    {
                        selectListener?.invoke(w.word)
                        break
                    }
                }
            }
            MotionEvent.ACTION_UP,
            MotionEvent.ACTION_CANCEL ->
            {
                upListener?.invoke()
            }
        }
        return super.onTouchEvent(event)
    }

    override fun onDraw(canvas: Canvas)
    {
        super.onDraw(canvas)
        var countHeight = 0
        for (w in words)
        {
            val word = w.word
            val rect = w.rect

            wordRect.setEmpty()
            paint.getTextBounds(word, 0, word.length, wordRect)
            val worldHeight = wordRect.height()
            val worldWidth = wordRect.width()

            rect.setEmpty()
            rect.top = countHeight
            countHeight += worldHeight + worldSpace
            rect.bottom = countHeight
            canvas.drawText(word, (mWidth / 2f) - (worldWidth / 2), countHeight.toFloat(), paint)
        }
    }

    private class Word(var word: String, val rect: Rect)

}