ListView A~Z快速索引这种效果在通信录和城市列表中经常看到,方便用户查找,是一种增加用户体验的好方法。
实现步骤:
1.自定义一个名叫SlideBar 的View。
2.在布局文件中加入这个自定义的View。
3. 在Activity中处理监听事件。
接下来讲讲我是怎样实现的:
先来看SlideBar这个类:
- package com.folyd.tuan.view;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.Typeface;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- /**
- * 自定义的View,实现ListView A~Z快速索引效果
- *
- * @author Folyd
- *
- */
- public class SlideBar extends View {
- private Paint paint = new Paint();
- private OnTouchLetterChangeListenner listenner;
- // 是否画出背景
- private boolean showBg = false;
- // 选中的项
- private int choose = -1;
- // 准备好的A~Z的字母数组
- public static String[] letters = { "#", "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" };
- // 构造方法
- public SlideBar(Context context) {
- super(context);
- }
- public SlideBar(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // 获取宽和高
- int width = getWidth();
- int height = getHeight() - 30;
- // 每个字母的高度
- int singleHeight = height / letters.length;
- if (showBg) {
- // 画出背景
- canvas.drawColor(Color.parseColor("#55000000"));
- }
- // 画字母
- for (int i = 0; i < letters.length; i++) {
- paint.setColor(Color.BLACK);
- // 设置字体格式
- paint.setTypeface(Typeface.DEFAULT_BOLD);
- paint.setAntiAlias(true);
- paint.setTextSize(20f);
- // 如果这一项被选中,则换一种颜色画
- if (i == choose) {
- paint.setColor(Color.parseColor("#F88701"));
- paint.setFakeBoldText(true);
- }
- // 要画的字母的x,y坐标
- float posX = width / 2 - paint.measureText(letters[i]) / 2;
- float posY = i * singleHeight + singleHeight;
- // 画出字母
- canvas.drawText(letters[i], posX, posY, paint);
- // 重新设置画笔
- paint.reset();
- }
- }
- /**
- * 处理SlideBar的状态
- */
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- final float y = event.getY();
- // 算出点击的字母的索引
- final int index = (int) (y / getHeight() * letters.length);
- // 保存上次点击的字母的索引到oldChoose
- final int oldChoose = choose;
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- showBg = true;
- if (oldChoose != index && listenner != null && index > 0
- && index < letters.length) {
- choose = index;
- listenner.onTouchLetterChange(event, letters[index]);
- invalidate();
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (oldChoose != index && listenner != null && index > 0
- && index < letters.length) {
- choose = index;
- listenner.onTouchLetterChange(event, letters[index]);
- invalidate();
- }
- break;
- case MotionEvent.ACTION_UP:
- default:
- showBg = false;
- choose = -1;
- if (listenner != null && index > 0 && index < letters.length)
- listenner.onTouchLetterChange(event, letters[index]);
- invalidate();
- break;
- }
- return true;
- }
- /**
- * 回调方法,注册监听器
- *
- * @param listenner
- */
- public void setOnTouchLetterChangeListenner(
- OnTouchLetterChangeListenner listenner) {
- this.listenner = listenner;
- }
- /**
- * SlideBar 的监听器接口
- *
- * @author Folyd
- *
- */
- public interface OnTouchLetterChangeListenner {
- void onTouchLetterChange(MotionEvent event, String s);
- }
- }
然后在布局文件中加入这个自定义的控件:
- <!-- 上面的代码省略掉了-->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content" >
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:divider="@drawable/line3" />
- <TextView
- android:id="@+id/float_letter"
- android:layout_width="80dp"
- android:layout_height="80dp"
- android:layout_gravity="center"
- android:background="#F88701"
- android:gravity="center"
- android:textSize="40sp"
- android:visibility="gone" />
- <com.folyd.tuan.view.SlideBar
- android:id="@+id/slideBar"
- android:layout_width="30dp"
- android:layout_height="wrap_content"
- android:layout_gravity="right|bottom" />
- </FrameLayout>
然后在Activity中注册监听事件:
- mSlideBar
- .setOnTouchLetterChangeListenner(new OnTouchLetterChangeListenner() {
- @Override
- public void onTouchLetterChange(MotionEvent event, String s) {
- float_letter.setText(s);
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE:
- float_letter.setVisibility(View.VISIBLE);
- break;
- case MotionEvent.ACTION_UP:
- default:
- float_letter.postDelayed(new Runnable() {
- @Override
- public void run() {
- float_letter.setVisibility(View.GONE);
- }
- }, 100);
- break;
- }
- int position = array.indexOf(s);//这个array就是传给自定义Adapter的
- mListView.setSelection(position);//调用ListView的setSelection()方法就可实现了
- }
- });
实现效果如下:
不过这样子有一个小小的Bug 。
请看图:
我的Bug是这样的:如果用户手指滑过A之后一直向上滑到,滑到切换城市的黄色标题栏(或滑过Z之后一直向下到滑划出屏幕),因为在整个FrameLayout内用户一直没有弹起手指,所以不能触发MotionEvent.ACTION_UP 这个状态,中间的TextView不能消失。可是我试了MotionEvent的其他一些状态,甚至在switch语句中后面加个default都没用。
---------------------------------------*******************************************************------------------------------------------------
我研究的时候发现只要showBg值为true,中间的字母就显示,而当showBg 的值为false的时候中间的字母就可以消失。只要SlideBar的状态为ACTION_DOWN和ACTION_MOVE 的时候showBg的值为true,而ACTION_UP的时候showBg的值就为false;
所以根据上面这个特征,我们只要把OnToucheLetterChange()这个回调函数的参数改一下就可以了。改成onTouchLetterChange(boolean isTouched, String s)
boolean类型的参数直接把showBg传过去就可以了。
改进后的代码如下:
- package com.folyd.tuan.view;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.Typeface;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- /**
- * 自定义的View,实现ListView A~Z快速索引效果
- *
- * @author Folyd
- *
- */
- publicclass SlideBar extends View {
- private Paint paint = new Paint();
- private OnTouchLetterChangeListenner listenner;
- // 是否画出背景
- privateboolean showBg = false;
- // 选中的项
- privateint choose = -1;
- // 准备好的A~Z的字母数组
- publicstatic String[] letters = { "#", "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" };
- // 构造方法
- public SlideBar(Context context) {
- super(context);
- }
- public SlideBar(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- @Override
- protectedvoid onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // 获取宽和高
- int width = getWidth();
- int height = getHeight();
- // 每个字母的高度
- int singleHeight = height / letters.length;
- if (showBg) {
- // 画出背景
- canvas.drawColor(Color.parseColor("#55000000"));
- }
- // 画字母
- for (int i = 0; i < letters.length; i++) {
- paint.setColor(Color.BLACK);
- // 设置字体格式
- paint.setTypeface(Typeface.DEFAULT_BOLD);
- paint.setAntiAlias(true);
- paint.setTextSize(20f);
- // 如果这一项被选中,则换一种颜色画
- if (i == choose) {
- paint.setColor(Color.parseColor("#F88701"));
- paint.setFakeBoldText(true);
- }
- // 要画的字母的x,y坐标
- float posX = width / 2 - paint.measureText(letters[i]) / 2;
- float posY = i * singleHeight + singleHeight;
- // 画出字母
- canvas.drawText(letters[i], posX, posY, paint);
- // 重新设置画笔
- paint.reset();
- }
- }
- /**
- * 处理SlideBar的状态
- */
- @Override
- publicboolean dispatchTouchEvent(MotionEvent event) {
- finalfloat y = event.getY();
- // 算出点击的字母的索引
- finalint index = (int) (y / getHeight() * letters.length);
- // 保存上次点击的字母的索引到oldChoose
- finalint oldChoose = choose;
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- showBg = true;
- if (oldChoose != index && listenner != null && index > 0
- && index < letters.length) {
- choose = index;
- listenner.onTouchLetterChange(showBg, letters[index]);
- invalidate();
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (oldChoose != index && listenner != null && index > 0
- && index < letters.length) {
- choose = index;
- listenner.onTouchLetterChange(showBg, letters[index]);
- invalidate();
- }
- break;
- case MotionEvent.ACTION_UP:
- showBg = false;
- choose = -1;
- if (listenner != null) {
- if (index <= 0) {
- listenner.onTouchLetterChange(showBg, "A");
- } elseif (index > 0 && index < letters.length) {
- listenner.onTouchLetterChange(showBg, letters[index]);
- } elseif (index >= letters.length) {
- listenner.onTouchLetterChange(showBg, "Z");
- }
- }
- invalidate();
- break;
- }
- returntrue;
- }
- /**
- * 回调方法,注册监听器
- *
- * @param listenner
- */
- publicvoid setOnTouchLetterChangeListenner(
- OnTouchLetterChangeListenner listenner) {
- this.listenner = listenner;
- }
- /**
- * SlideBar 的监听器接口
- *
- * @author Folyd
- *
- */
- publicinterface OnTouchLetterChangeListenner {
- void onTouchLetterChange(boolean isTouched, String s);
- }
- }
- mSlideBar
- .setOnTouchLetterChangeListenner(new OnTouchLetterChangeListenner() {
- @Override
- publicvoid onTouchLetterChange(boolean isTouched, String s) {
- float_letter.setText(s);
- if (isTouched) {
- float_letter.setVisibility(View.VISIBLE);
- } else {
- float_letter.postDelayed(new Runnable() {
- @Override
- publicvoid run() {
- float_letter.setVisibility(View.GONE);
- }
- }, 100);
- }
- int position = array.indexOf(s);
- mListView.setSelection(position);
- }
- });
这样就解决Bug了。哈哈。
良心的公众号,更多精品文章,不要忘记关注哈
《Android和Java技术栈》