使用自定义控件和ListView实现快速索引功能

    实现的功能:主界面分为右侧的自定义控件,中间为一个ListView,如下图:


   点击主界面右侧的A-Z字符,快递定位到ListView的item中汉字以该字母开头的数据。

使用到第三方jar包,使用该jar包的由字符串得到拼音的功能。

    实现的过程:

                           1.创建自定义控件然后实现主界面右侧A-Z的绘画

       2.处理A-Z的onTouch事件

       3.在自定义控件中提供点击事件的监听

       4.使用jar实现将字符串转换成汉字

       5.进行分组排序展示

       6.实现点击自定义控件对listView的选择


1.创建自定义控件:

    自定义QUICKIndexBar控件,然后继承View,在初始化方法中定义一个画笔然后设置其颜色和类型:

 public void setListener(OnLetterUpdateListener listener) {
        this.listener = listener;
    }
    public QuickIndexBar(Context context) {
        this(context, null);
    }

    public QuickIndexBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public QuickIndexBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //消除锯齿
        mPaint.setColor(Color.WHITE);//设置颜色
        mPaint.setTypeface(Typeface.DEFAULT_BOLD);//设置黑体字体类型
<pre name="code" class="java">        mPaint.setTextSize(40);//设置字体大小
 }

    画笔定义了之后就是对A-Z进行绘画,重写onDraw方法可实现对界面的绘画,绘画的难点是是确定A-Z的坐标,A-Z的横向位置都是一样的,所有可以首先拿到存放A-Z单元格的宽高,然后得到A-Z的X坐标,经过画图可以很直接的观察到,A-Z每个相邻的字母Y坐标的距离都是又规律的,然后就可以很轻松的拿到每一个字符的Y坐标,也正是有这个规律可以从Y坐标求出对应的字符。 

x = (单元格宽度-字符宽度)/2

y = (单元格高度+字符高度)/2+单元格高度*单元格个数



 <span style="font-size:14px;">/**
     * 获取到单元格的宽高
     * @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;
    }
</span><pre name="code" class="java"><span style="font-size:14px;">  /**
     * 绘制A_Z
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(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(touchIndex == i ? Color.GRAY : Color.WHITE);
            // 绘制文本A-Z
            canvas.drawText(text, x, y, mPaint);
        }</span>

 

 
A-Z绘制完毕之后,要进行A-Z的点击的点击事件,实现的是点击A-Z任意字符,弹出一个Toast提示 

2.处理onTouch事件:

   首先根据用户手指按下,拿到Y坐标,然后用Y坐标/单元格高度,求出按下的是哪个单元格,然后求出是哪个字符,再用Toast进行提示,处理了DOWN事件后,进行MOVE事件的处理,由于要多次处理滑动事件,所有要reture true让该空间处理触摸事件,滑动事件判断哪个字符和DOWN事件一样,代码如下:

/**
	 * 处理触摸事件
	 */
	int touchIndex = -1;
	@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 != touchIndex) {
						if(listener != null){
							listener.onLetterUpdate(LETTERS[index]);
						}
						Log.d(TAG, "onTouchEvent: " + LETTERS[index]);
						
						touchIndex = index;
					}
				}
				
				break;
			case MotionEvent.ACTION_MOVE:
				index = (int) (event.getY() / cellHeight);
				if(index >= 0 && index < LETTERS.length){
					// 判断是否跟上一次触摸到的一样
					if(index != touchIndex){
						
						if(listener != null){
							listener.onLetterUpdate(LETTERS[index]);
						}
						Log.d(TAG, "onTouchEvent: " + LETTERS[index]);
						
						touchIndex = index;
					}
				}
				break;
			case MotionEvent.ACTION_UP:
				touchIndex = -1;
				break;
	
			default:
				break;
		}
		invalidate();
		
		return true;
	}
   3.在自定义控件中提供一个 接口供主界面调用

    定义一个接口和方法供主界面找到ListView的索引,这个接口中的方法在触摸事件中调用。

<span style="font-size:14px;"> /**
     * 暴露一个字母的监听
     */
    public interface OnLetterUpdateListener{
        void onLetterUpdate(String letter);
    }
    private OnLetterUpdateListener listener;

    public OnLetterUpdateListener getListener() {
        return listener;
    }
    /**
     * 设置字母更新监听
     * @param listener
     */
    public void setListener(OnLetterUpdateListener listener) {
        this.listener = listener;
    }</span>
   4.使用jar首先一个工具方法,这个工具方法实现了输入字符串得到汉语拼音的回调
<span style="font-size:14px;">public class PinyinUtils {
	/**
	 * 根据传入的字符串(包含汉字),得到拼音
	 * @param str 字符串
	 * @return
	 */
	public static String getPinyin(String str) {
		
		HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
		format.setCaseType(HanyuPinyinCaseType.UPPERCASE);
		format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
		
		StringBuilder sb = new StringBuilder();
		
		char[] charArray = str.toCharArray();
		for (int i = 0; i < charArray.length; i++) {
			char c = charArray[i];
			// 如果是空格, 跳过
			if(Character.isWhitespace(c)){
				continue;
			}
			if(c >= -127 && c < 128){
				// 肯定不是汉字
				sb.append(c);
			}else {
				String s = "";
				try {
					// 通过char得到拼音集合. 单 -> dan, shan 
					s = PinyinHelper.toHanyuPinyinStringArray(c, format)[0];
					sb.append(s);
				} catch (BadHanyuPinyinOutputFormatCombination e) {
					e.printStackTrace();
					sb.append(s);
				}
			}
		}
		
		return sb.toString();
	}

}
</span>

 5.对ListView的数据进行分组排序处理

首先创建一个人名类,在该类中有两个变量name和pinyin变量,在构造方法中只传入人名然后使用工具类得到拼音,然后创建一个类在类中写入所有人名的数据,然后提供给listView,然后在MainAcitivity中,拿到该数据并排好序后,使用ListView进行展示(具体代码看附件)。

<span style="font-size:14px;">/**
     * 根据输入的集合对集合中的数据进行排序
     * @param persons
     */
    private void fillAndSortData(ArrayList<Person> persons) {
        for (int i = 0; i < Cheeses.NAMES.length; i++) {
            String name = Cheeses.NAMES[i];
            persons.add(new Person(name));
        }
        //进行排序
        Collections.sort(persons);
    }
</span><pre name="code" class="java"><span style="font-size:14px;">     //存储人名和拼音
        mPersons = new ArrayList<>();
        // 填充数据 , 排序
        fillAndSortData(mPersons);
        HaoHanAdapter adapter = new HaoHanAdapter(this, mPersons);
        list.setAdapter(adapter);</span>


 

  6.最后进行的是将自定义控件和ListView的绑定。

将自定义控件为ListView进行绑定需要用到自定义控件中改变字母监听的接口,该接口中点击A-Z可以带到A-Z的值,然后将A-Z的值与ListView数据中的拼音进行比较,若相同,则跳转该处,这样就实现了点击A-Z进行对ListView中数据的索引。

private void initData() {
        /**
         * 实现索引
         */
        bar.setListener(new QuickIndexBar.OnLetterUpdateListener() {
            @Override
            public void onLetterUpdate(String letter) {
//                Utils.showToast(getApplicationContext(), letter);
                showLetter(letter);
                // 根据字母定位ListView, 找到集合中第一个以letter为拼音首字母的对象,得到索引
                for (int i = 0; i <mPersons.size() ; i++) {
                    Person person = mPersons.get(i);
                    String l = person.getPinyin().charAt(0)+"";
                    if (TextUtils.equals(letter,l)){
                        list.setSelection(i);
                        break;
                    }
                }
            }
        });
<pre name="code" class="java"> //显示字母
    private void showLetter(String letter) {
        tv_centent.setVisibility(View.VISIBLE);
        tv_centent.setText(letter);

        mHandler.removeCallbacksAndMessages(null);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                tv_centent.setVisibility(View.GONE);
            }
        },2000 );
    }


 

最终结果:

源码下载地址:链接:http://pan.baidu.com/s/1dFDKGnf 密码:6ed0


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值