zoukankan      html  css  js  c++  java
  • 实现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()方法就可实现了
    					}
    				});


    实现效果如下:


         


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

    请看图:



    有人知道是什么Bug吗?

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



  • 相关阅读:
    《构建之法》阅读报告
    教务管理系统类图及数据库E/R图
    设计模式:抽象工厂
    结对项目:四则运算程序测试
    Leetcode笔记之57和为s的连续正数序列
    Leetcode笔记之1103分糖果 II
    Leetcode笔记之199二叉树的右视图
    每日Scrum(9)
    每日Scrum(7)
    每日Scrum(6)
  • 原文地址:https://www.cnblogs.com/riskyer/p/3281393.html
Copyright © 2011-2022 走看看