实现ListView A~Z快速索引

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()方法就可实现了

					}

				});


实现效果如下:

 


实现ListView A~Z快速索引

     实现ListView A~Z快速索引


不过这样子有一个小小的Bug 。360的某个APP也有这个Bug。偷笑那个APP99%也是用这种方式实现的。

请看图:

实现ListView A~Z快速索引


有人知道是什么Bug吗?

我的Bug是这样的:如果用户手指滑过A之后一直向上滑到,滑到切换城市的黄色标题栏(或滑过Z之后一直向下到滑划出屏幕),因为在整个FrameLayout内用户一直没有弹起手指,所以不能触发MotionEvent.ACTION_UP 这个状态,中间的TextView不能消失。可是我试了MotionEvent的其他一些状态,甚至在switch语句中后面加个default都没用,暂时不知道怎样解决这个小Bug,有知道的朋友还望多多指教。谢谢。



 

你可能感兴趣的:(ListView)