1.效果实现分析
交互控件,onTouch()事件
1.1 实现默认效果
用画笔绘制[A-Z],自定义View
/**
* 根据ASCII 表动态生成A~Z
* */
private val charArray by lazy {
mutableListOf<String>().apply {
val start = 65
val end = 65 + (26)
for (i in start until end) {
add(i.toChar().toString())
}
add("#")
}
}
复写onMeasure()方法:指定宽高
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//计算指定宽度=左右padding+字母宽度(取决于画笔)
val textWidth = paint.measureText("A")
val weight = (paddingLeft + textWidth + paddingRight).toInt()
//高度可以直接获取
val height = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(weight, height)
}
字母不居中
//画26个字母 x绘制在最中间=宽度/2-文字/2
val x=width/2-paint.measureText(content)/2
1.2 处理交互手指在上面触摸的效果
效果是: 手指触摸高亮当前位置
override fun onTouchEvent(e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_MOVE, MotionEvent.ACTION_DOWN -> {
//计算出当前触摸字母=>获取当前的位置
val currentMoveY = e.y
//位置=currentMoveY/字母高度,通过位置获取字母
val currentPosition = (currentMoveY / itemHeight).toInt()
currentTouchLetter = when {
(currentPosition < 0 )-> 0
(currentPosition > charArray.size - 1) -> charArray.size - 1
else -> currentPosition
}
//重新绘制
invalidate()
}
}
return true
}
中间显示当前选择的字母
2.完整代码
<declare-styleable name="LetterSideBar">
<attr name="letterColor" format="color"/>
<attr name="letterSize" format="dimension"/>
</declare-styleable>
class LetterSideBar @JvmOverloads constructor(
context: Context,
attr: AttributeSet? = null,
defStyle: Int = 0
) : View(context, attr, defStyle) {
private val letterColor: Int
private val letterSize: Int
private val normalPaint: Paint
private val highlightPaint: Paint
lateinit var currentTextCallBack:TouchLetterListener
/**
* 当前触摸位置的字母
* */
private var currentTouchLetter = 0
init {
context.obtainStyledAttributes(attr, R.styleable.LetterSideBar).apply {
val defaultColor = Color.BLUE
val defaultTextSize = context.sp2px(15)
letterColor = getColor(R.styleable.LetterSideBar_letterColor, defaultColor)
letterSize = getDimensionPixelSize(R.styleable.LetterSideBar_letterSize, defaultTextSize)
normalPaint = producePaint(letterSize, letterColor)
highlightPaint = producePaint(letterSize, Color.RED)
recycle()
}
}
private fun producePaint(size: Int, paintColor: Int) =
with(Paint()) {
isAntiAlias = true
color = paintColor
textSize = size.toFloat()
this
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//计算指定宽度=左右padding+字母宽度(取决于画笔)
val textWidth = normalPaint.measureText("A")
val weight = (paddingLeft + textWidth + paddingRight).toInt()
//高度可以直接获取
val height = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(weight, height)
}
override fun onTouchEvent(e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_MOVE-> {
//计算出当前触摸字母=>获取当前的位置
val currentMoveY = e.y
//位置=currentMoveY/字母高度,通过位置获取字母
val currentPosition = (currentMoveY / itemHeight).toInt()
val safePosition=when {
(currentPosition < 0 )-> 0
(currentPosition > charArray.size - 1) -> charArray.size - 1
else -> currentPosition
}
if (safePosition==currentPosition){
return true
}
currentTouchLetter = safePosition
if (::currentTextCallBack.isInitialized){
currentTextCallBack.showLetter(charArray[currentTouchLetter])
currentTextCallBack.isShowText(true)
}
//重新绘制
invalidate()
}
MotionEvent.ACTION_UP->currentTextCallBack.isShowText(false)
}
return true
}
/**
* 字母的高度
* */
private val itemHeight by lazy {
(height - paddingTop - paddingBottom) / charArray.size
}
/**
* 知道每个字母的中心位置
*
* 1.字母的高度一半
*
* 2.字母高度一般+前面字符的高度
*
* 基线,基于中心位置
* */
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
for (i in charArray.indices) {
val content = charArray[i]
//画26个字母 x绘制在最中间=宽度/2-文字/2
val x = width / 2 - normalPaint.measureText(content) / 2
val letterCenterY = i * itemHeight + itemHeight / 2 + paddingTop
val dy = with(normalPaint.fontMetricsInt) {
(bottom - top) / 2 - bottom
}
val currentPaint = (if (currentTouchLetter == i) highlightPaint else normalPaint)
val baseLine = (letterCenterY + dy).toFloat()
canvas.drawText(content, x, baseLine, currentPaint)
}
}
/**
* 根据ASCII 表动态生成A~Z
* */
private val charArray by lazy {
mutableListOf<String>().apply {
val start = 65
val end = 65 + (26)
for (i in start until end) {
add(i.toChar().toString())
}
add("#")
}
}
interface TouchLetterListener{
fun isShowText(flag:Boolean)
fun showLetter(letter:String)
}
}