分析:这是一个很常见的功能,类似微信通录,实现方式也比较简单,先是用画笔画确定文字的中心位置,然后计算出出文字的宽度,接着遍历26个字母。最后通过onTouchEvent事件,获取坐标Y值,从而确定选中字母,最后高亮字母,invalidate()方法绘制。关键一点就是在onMeasure中重新计算控件的宽度,还有就是要防止选中下标超出范围,具体实现如下: /** * Email [email protected] * Created by Michael Chen on 2018/6/3. * Version 1.0 * Description:字母索引练习 */ public class LetterSideBar extends View { //定义26个字母 public static String[] mLetters = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "#"}; private Paint mPaint; //当前触摸的位置字母 private String mCurrentLetter="A"; private String mCurrentTouchListener; private int cellHeight; public LetterSideBar(Context context) { this(context,null); } public LetterSideBar(Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); } public LetterSideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint=new Paint(); mPaint.setAntiAlias(true); mPaint.setTextSize(sp2px(12)); mPaint.setColor(Color.BLUE); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //计算指定宽度=左右的padding+字母的宽度 int textWidth= (int) mPaint.measureText("A"); int width=getPaddingLeft()+getPaddingRight()+textWidth; //高度可以直接获取 int height = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(width,height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //画26个字母 cellHeight = (getHeight() - getPaddingBottom() - getPaddingTop()) / mLetters.length; for (int i = 0; i < mLetters.length; i++) { //当前的letterCenterY是cellHeight/2+之前所有单元格的高度 int letterCenterY=getPaddingTop()+i* cellHeight + cellHeight /2; //计算基线 Paint.FontMetrics fontMetrics = mPaint.getFontMetrics(); int dy= (int) ((fontMetrics.bottom-fontMetrics.top)/2-fontMetrics.bottom); int baseLine=letterCenterY+dy; int textWidth= (int) mPaint.measureText(mLetters[i]); //x的左下标为控件宽度/2-文字宽度/2; int x=getWidth()/2-textWidth/2; //当前字母高亮 if (mCurrentLetter.equals(mLetters[i])){ mPaint.setColor(Color.RED); canvas.drawText(mLetters[i],x,baseLine,mPaint); }else { mPaint.setColor(Color.BLUE); canvas.drawText(mLetters[i], x, baseLine, mPaint); } } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: //当前触摸位置,计算选中的字母 float currentY=event.getY(); //位置=currentY/单元格高度 int currentPosition= (int) (currentY/cellHeight); if (currentPosition<=0){ currentPosition=0; } if (currentPosition>=mLetters.length-1){ currentPosition=mLetters.length-1; } mCurrentLetter=mLetters[currentPosition]; if(mListener!=null){ mListener.touch(mCurrentLetter,false); } invalidate(); break; case MotionEvent.ACTION_UP: if(mListener!=null){ mListener.touch(mCurrentLetter,false); } break; } return true; } // sp 转 px private float sp2px(int sp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } private LetterTouchListener mListener; public interface LetterTouchListener{ void touch(String letter,boolean isTouch); } }