-
`// 获取文本的高度`
-
`Rect bounds = new Rect();// 矩形`
-
`mPaint.getTextBounds(text, 0, text.length(), bounds);`
-
`int textHeight = bounds.height();`
-
`int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i * cellHeight);`
-
`// 根据按下的字母, 设置画笔颜色`
-
`mPaint.setColor(mLastTouchIndex == i ? mSelectColor : mNormalColor);`
-
`// 绘制文本A-Z`
-
`canvas.drawText(text, x, y, mPaint);`
-
`}`
-
`}`
-
`@Override`
-
`public boolean onTouchEvent(MotionEvent event) {`
-
`int index = -1;`
-
`switch (MotionEventCompat.getActionMasked(event)) {`
-
`case MotionEvent.ACTION_DOWN:`
-
`// 获取当前触摸到的字母索引`
-
`index = (int) (event.getY() / cellHeight);`
-
`if (index >= 0 && index < LETTERS.length) {`
-
`// 判断是否跟上一次触摸到的一样,不一样才进行回调`
-
`if (index != mLastTouchIndex) {`
-
`if (listener != null) {`
-
`//`
-
`listener.onLetterUpdate(LETTERS[index]);`
-
`}`
-
`Log.d(TAG, "onTouchEvent: " + LETTERS[index]);`
-
`//记录上一次触摸的Index为当前的index;`
-
`mLastTouchIndex = index;`
-
`}`
-
`}`
-
`break;`
-
`case MotionEvent.ACTION_MOVE:`
-
`index = (int) (event.getY() / cellHeight);`
-
`if (index >= 0 && index < LETTERS.length) {`
-
`// 判断是否跟上一次触摸到的一样`
-
`if (index != mLastTouchIndex) {`
-
`if (listener != null) {`
-
`listener.onLetterUpdate(LETTERS[index]);`
-
`}`
-
`Log.d(TAG, "onTouchEvent: " + LETTERS[index]);`
-
`mLastTouchIndex = index;`
-
`}`
-
`}`
-
`break;`
-
`case MotionEvent.ACTION_UP:`
-
`// 手指抬起的时候重置`
-
`mLastTouchIndex = -1;`
-
`break;`
-
`default:`
-
`break;`
-
`}`
-
`//调用这个方法会重新调用draw方法,重新绘制`
-
`invalidate();`
-
`return true;`
-
`}`
-
`/**`
-
`* 当大小 改变的时候会回调这个方法,`
-
`* 这里我们就不主动调用measure()方法了`
-
`*`
-
`* @param w`
-
`* @param h`
-
`* @param oldw`
-
`* @param oldh`
-
`*/`
-
`@Override`
-
`protected void onSizeChanged(int w, int h, int oldw, int oldh) {`
-
`super.onSizeChanged(w, h, oldw, oldh);`
-
`// 获取单元格的宽和高`
-
`cellWidth = getMeasuredWidth();`
-
`int mHeight = getMeasuredHeight();`
-
`cellHeight = mHeight * 1.0f / LETTERS.length;`
-
`}`
-
}
代码 解析
- 代码其实不长,加上一些注释总共才180多行,总体来说,思路分分为以下几个步骤
-
在构造方法里面初始化画笔,同时为了使用方便,我们封装了自定义属性
-
public QuickIndexBar(Context context, AttributeSet attrs, int defStyle) {
-
`super(context, attrs, defStyle);`
-
`mContext = context;`
-
`//初始化自定义属性`
-
`obtainAttrs(attrs);`
-
`// 初始化画笔`
-
`mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);`
-
`float textSize = UIUtils.dip2px(15, mContext);`
-
`mPaint.setTextSize(textSize);`
-
`mPaint.setTypeface(Typeface.DEFAULT_BOLD);`
-
}
-
`private void obtainAttrs(AttributeSet attrs) {`
-
`TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.QuickIndexBar);`
-
`int selectColor = typedArray.getColor(R.styleable.QuickIndexBar_select_color, -1);`
-
`if (selectColor != -1) {`
-
`mSelectColor = selectColor;`
-
`}`
-
`int normalColor = typedArray.getColor(R.styleable.QuickIndexBar_normal_color, -1);`
-
`if (normalColor != -1) {`
-
`mNormalColor = normalColor;`
-
`}`
-
`typedArray.recycle();`
-
`}`
-
接着我们在onSizeChange 方法里面拿到我们时间的宽度和高度,有人可能会问了为什么不在onMeasure里面获取了,其实在onMeasure方法里面获取是可以的,只不过我们还需要调用一下measure方法而已,在onSizeChnage方法里面,我们直接调用
-
cellWidth = getMeasuredWidth();
即可获取到我们需要的宽度和高度。用起来比较方便,不过更多的是为了让大家知道View有这一个方法存在以及怎么使用它
顺便我们来看一下google官方对onSizeChange方法的解释
This is called during layout when the size of this view has changed. If you were just added to the view hierarchy, you’re called with the old values of 0.
从官方的解释我们可以知道这个方法是在onLayout方法中当大小改变的时候会调用这个方法,因此我们直接调用getMeasuredWidth();是可以获取得到宽度的,因为onMeasure 是先于onLayout方法调用的。
3. 接着我们重写onDraw方法,在onDraw方法我们所做的工作就是绘制 我们需要的26个字母
-
`protected void onDraw(Canvas canvas) {`
-
`for (int i = 0; i < LETTERS.length; i++) {`
-
`String text = LETTERS[i];`
-
`// 计算坐标`
-
`int x = (int) (cellWidth / 2.0f - mPaint.measureText(text) / 2.0f);`
-
`// 获取文本的高度`
-
`Rect bounds = new Rect();// 矩形`
-
`mPaint.getTextBounds(text, 0, text.length(), bounds);`
-
`int textHeight = bounds.height();`
-
`int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i * cellHeight);`
-
`// 根据按下的字母, 设置画笔颜色`
-
`mPaint.setColor(mLastTouchIndex == i ? mSelectColor : mNormalColor);`
-
`// 绘制文本A-Z`
-
`canvas.drawText(text, x, y, mPaint);`
-
`}`
-
`}`
-
讲到这里,我们的工作已经完成一大半了,接着就是处理我们是按下或者一个字母了,我们重写onTouchEvent方法,并且return true;是为了保证 Action_down动作按下以后,Action_move以后的动作能够顺利接受到,这涉及到View的事件分发机制,有空的话我会尝试总结一下,这里就不说了
-
// 获取当前触摸到的字母索引
-
index = (int) (event.getY() / cellHeight);
同时我们记录下我们当前是触摸或者按下哪一个字母
-
mLastTouchIndex = index;
-
知道了我们当前是触摸或者按下哪一个字母了,那我们要怎样将这些信息暴露出去了,不难想象就是采用接口回调的方法,为此我们提供了这样一个接口
-
/**
-
* 暴露一个字母的监听
-
*/
-
public interface OnLetterUpdateListener {
-
`void onLetterUpdate(String letter);`
-
}
并且提供了设置监听器的方法,这样我们就成功将我们的按下字母的信息提供给外界了
-
public void setListener(OnLetterUpdateListener listener) {
-
`this.listener = listener;`
-
}
详细代码如下
-
int index = -1;
-
switch (MotionEventCompat.getActionMasked(event)) {
-
`case MotionEvent.ACTION_DOWN:`
-
`// 获取当前触摸到的字母索引`
-
`index = (int) (event.getY() / cellHeight);`
-
`if (index >= 0 && index < LETTERS.length) {`
-
`// 判断是否跟上一次触摸到的一样,不一样才进行回调`
-
`if (index != mLastTouchIndex) {`
-
`if (listener != null) {`
-
`//`
-
`listener.onLetterUpdate(LETTERS[index]);`
-
`}`
-
`Log.d(TAG, "onTouchEvent: " + LETTERS[index]);`
-
`//记录上一次触摸的Index为当前的index;`
-
`mLastTouchIndex = index;`
-
`}`
-
`}`
-
`break;`
-
`case MotionEvent.ACTION_MOVE:`
-
`index = (int) (event.getY() / cellHeight);`
-
`if (index >= 0 && index < LETTERS.length) {`
-
`// 判断是否跟上一次触摸到的一样`
-
`if (index != mLastTouchIndex) {`
-
`if (listener != null) {`
-
`listener.onLetterUpdate(LETTERS[index]);`
-
`}`
-
`Log.d(TAG, "onTouchEvent: " + LETTERS[index]);`
-
`mLastTouchIndex = index;`
-
`}`
-
`}`
-
`break;`
-
`case MotionEvent.ACTION_UP:`
-
`// 手指抬起的时候重置`
-
`mLastTouchIndex = -1;`
-
`break;`
-
`default:`
-
`break;`
-
}
-
//调用这个方法会重新调用draw方法,重新绘制
-
invalidate();
-
return true;
到此 QuickBarView的源码分析为止,下面我们来学习一下是怎样结合ListView使用的
下面我贴出核心代码,想仔细了解的请点击源码下载 源码下载地址:
-
mQuickIndexBar.setListener(new OnLetterUpdateListener() {
-
`@Override`
-
`public void onLetterUpdate(String letter) {`
-
`// UIUtils.showToast(getApplicationContext(), letter);`
-
`showLetter(letter);`
-
`// 根据字母定位ListView, 找到集合中第一个以letter为拼音首字母的对象,得到索引`
-
`for (int i = 0; i < persons.size(); i++) {`
-
`Person person = persons.get(i);`
-
`String l = person.getPinyin().charAt(0) + "";`
-
`if (TextUtils.equals(letter, l)) {`
-
`// 匹配成功`
-
`mListView.setSelection(i);`
-
`break;`
-
`}`
-
`}`
-
`}`
-
});
思路解析 如下
1. 在我们的List数据里面查找是否有有相应的首字母是触摸的字母,有的话返回相应的index,
2. 然后再调用ListView的setSelection(i)方法选中哪一个Item
mListView.setSelection(i);
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-c0a541DO-1715902154683)]
[外链图片转存中…(img-IqqAwHuY-1715902154686)]
[外链图片转存中…(img-M8Vdv6c2-1715902154686)]
[外链图片转存中…(img-0pGlYYOW-1715902154687)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!