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 。360的某个APP也有这个Bug。那个APP99%也是用这种方式实现的。
请看图:
有人知道是什么Bug吗?
我的Bug是这样的:如果用户手指滑过A之后一直向上滑到,滑到切换城市的黄色标题栏(或滑过Z之后一直向下到滑划出屏幕),因为在整个FrameLayout内用户一直没有弹起手指,所以不能触发MotionEvent.ACTION_UP 这个状态,中间的TextView不能消失。可是我试了MotionEvent的其他一些状态,甚至在switch语句中后面加个default都没用,暂时不知道怎样解决这个小Bug,有知道的朋友还望多多指教。谢谢。