- 前言
最近项目需要做一个地区首字母a-z排序的效果,记录一下自己如何实现的.
先看下效果图:
- 分析
这种效果自己实现还是第一次;之前见过这种效果:
这些字母都是onDraw画上去的;只要知道每个字母的left,top,right,bottom就能知道它的具体位置,所以onMeasure方法中要确定每个单元格的宽高.文字排序可以先把汉字转换成拼音,再去比较首字母的顺序(特殊地区特殊处理,比如重庆);
具体看下是如何确定字母的位置:
于是乎,代码就出来了:
1 public class QuickIndexBar extends View { 2 3 private OnLetterUpdateListener onLetterUpdateListener; 4 5 public interface OnLetterUpdateListener{ 6 void onLetterUpdate(String letter); 7 } 8 9 public OnLetterUpdateListener getOnLetterUpdateListener() { 10 return onLetterUpdateListener; 11 } 12 13 public void setOnLetterUpdateListener( 14 OnLetterUpdateListener onLetterUpdateListener) { 15 this.onLetterUpdateListener = onLetterUpdateListener; 16 } 17 private static final String[] LETTERS = new String[]{ 18 "A", "B", "C", "D", "E", "F", 19 "G", "H", "I", "J", "K", "L", 20 "M", "N", "O", "P", "Q", "R", 21 "S", "T", "U", "V", "W", "X", 22 "Y", "Z" 23 }; 24 25 private Paint paint; 26 27 // 单元格宽度 28 private int cellWidth; 29 30 // 单元格高度 31 private float cellHeight; 32 33 34 public QuickIndexBar(Context context) { 35 this(context, null); 36 } 37 38 public QuickIndexBar(Context context, AttributeSet attrs) { 39 this(context, attrs, 0); 40 } 41 42 public QuickIndexBar(Context context, AttributeSet attrs, int defStyle) { 43 super(context, attrs, defStyle); 44 // 创建一个抗锯齿的画笔 45 paint = new Paint(Paint.ANTI_ALIAS_FLAG); 46 // 画笔文本加粗 47 paint.setTypeface(Typeface.DEFAULT_BOLD); 48 // 颜色 49 paint.setColor(Color.WHITE); 50 } 51 52 @Override 53 protected void onDraw(Canvas canvas) { 54 55 // 遍历26个英文字母, 计算坐标, 进行绘制 56 for (int i = 0; i < LETTERS.length; i++) { 57 String letter = LETTERS[i]; 58 59 // 计算x坐标 60 float x = cellWidth * 0.5f - paint.measureText(letter) * 0.5f; 61 // 计算y坐标 62 Rect bounds = new Rect(); 63 // 获取文本的矩形区域 64 paint.getTextBounds(letter, 0, letter.length(), bounds); 65 66 float y = cellHeight * 0.5f + bounds.height() * 0.5f + i * cellHeight; 67 68 // 绘制文本 69 canvas.drawText(letter, x, y, paint); 70 } 71 } 72 private int lastIndex = -1; 73 74 @Override 75 public boolean onTouchEvent(MotionEvent event) { 76 77 float y; 78 int currentIndex; 79 80 switch (event.getAction()) { 81 case MotionEvent.ACTION_DOWN: 82 // 获取被点击到的字母索引 83 y = event.getY(); 84 // 根据y值, 计算当前按下的字母位置 85 currentIndex = (int) (y / cellHeight); 86 if(currentIndex != lastIndex){ 87 if(currentIndex >= 0 && currentIndex < LETTERS.length){ 88 String letter = LETTERS[currentIndex]; 89 if(onLetterUpdateListener != null){ 90 onLetterUpdateListener.onLetterUpdate(letter); 91 } 92 System.out.println("letter: " + letter); 93 // 记录上一次触摸的字母 94 lastIndex = currentIndex; 95 } 96 } 97 98 break; 99 case MotionEvent.ACTION_MOVE:100 // 获取被点击到的字母索引101 y = event.getY();102 // 根据y值, 计算当前按下的字母位置103 currentIndex = (int) (y / cellHeight);104 if(currentIndex != lastIndex){105 if(currentIndex >= 0 && currentIndex < LETTERS.length){106 String letter = LETTERS[currentIndex];107 if(onLetterUpdateListener != null){108 onLetterUpdateListener.onLetterUpdate(letter);109 }110 System.out.println("letter: " + letter);111 // 记录上一次触摸的字母112 lastIndex = currentIndex;113 }114 }115 116 break;117 case MotionEvent.ACTION_UP:118 lastIndex = -1;119 break;120 default:121 break;122 }123 124 return true;125 }126 127 @Override128 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {129 super.onMeasure(widthMeasureSpec, heightMeasureSpec);130 int mHeight = getMeasuredHeight();131 cellWidth = getMeasuredWidth();132 cellHeight = mHeight * 1.0f / LETTERS.length;133 }134 135 136 137 }
这种竖直的简单快速索引就搞定了,此外还添加了触摸和点击的监听
- 实现
再来看下我们要的效果图那种效果是如何实现的;主要区别就是字母所在位置和触摸位置的差异,偷点懒,直接上代码了:
1 @Override 2 protected void onDraw(Canvas canvas) { 3 4 // 遍历26个英文字母, 计算坐标, 进行绘制 5 for (int i = 0; i < LETTERS.length; i++) { 6 String letter = LETTERS[i]; 7 8 // 计算x坐标 9 // float x = cellWidth * 0.5f - paint.measureText(letter) * 0.5f;10 float y = cellHeight * 0.5f + paint.measureText(letter) * 0.5f;11 // 计算y坐标12 Rect bounds = new Rect();13 // 获取文本的矩形区域14 paint.getTextBounds(letter, 0, letter.length(), bounds);15 16 // float y = cellHeight * 0.5f + bounds.height() * 0.5f + i * cellHeight;17 float x = cellWidth * 0.5f + bounds.width() * 0.5f + i * cellWidth;18 // 绘制文本19 canvas.drawText(letter, x, y, paint);20 }21 }22 23 private int lastIndex = -1;24 25 @Override26 public boolean onTouchEvent(MotionEvent event) {27 28 float x;29 int currentIndex;30 31 switch (event.getAction()) {32 case MotionEvent.ACTION_DOWN:33 // 获取被点击到的字母索引34 x = event.getX();35 // 根据x值, 计算当前按下的字母位置36 currentIndex = (int) (x / cellWidth);37 if (currentIndex != lastIndex) {38 if (currentIndex >= 0 && currentIndex < LETTERS.length) {39 String letter = LETTERS[currentIndex];40 if (onLetterUpdateListener != null) {41 onLetterUpdateListener.onLetterUpdate(letter);42 }43 // 记录上一次触摸的字母44 lastIndex = currentIndex;45 }46 }47 48 break;49 case MotionEvent.ACTION_MOVE:50 // 获取被点击到的字母索引51 x = event.getX();52 // 根据y值, 计算当前按下的字母位置53 currentIndex = (int) (x / cellWidth);54 if (currentIndex != lastIndex) {55 if (currentIndex >= 0 && currentIndex < LETTERS.length) {56 String letter = LETTERS[currentIndex];57 if (onLetterUpdateListener != null) {58 onLetterUpdateListener.onLetterUpdate(letter);59 }60 // 记录上一次触摸的字母61 lastIndex = currentIndex;62 }63 }64 65 break;66 case MotionEvent.ACTION_UP:67 lastIndex = -1;68 break;69 default:70 break;71 }72 73 return true;74 }75 76 @Override77 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {78 super.onMeasure(widthMeasureSpec, heightMeasureSpec);79 int mWidth = getMeasuredWidth();80 cellWidth = mWidth * 1.0f / LETTERS.length;81 cellHeight = getMeasuredHeight();82 }
可以看到主要区别就是单元格的宽度和高度相应改变了;后面会补充一张计算草图.