/**
* @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()
}
Android 字母导航栏
这篇博客详细介绍了如何创建一个自定义的字母导航栏View,包括绘制A到Z的字母,处理触摸事件以及设置选中状态。代码示例展示了如何在Android中实现这一功能,涉及Android视图绘制、触摸事件处理等技术。
摘要由CSDN通过智能技术生成