自定义控件之A-Z快速检索QuickIndexBar

---------快速检索--------------
1.应用场景:联系人,好友列表,商品等列表的快速定位和搜索
2.实现逻辑:
a.右边是自定义QuickIndexBar,它能获取触摸它的时候当前所触摸到的字母;
 绘制文本x坐标: width/2;
 绘制文本y坐标: 格子高度的一半 + 文本高度的一半 + position*格子高度
 计算触摸点对应的字母:根据触摸点的y坐标除以cellHeight,得到的值就是字母对应的索引;
 
b.左边是listview,它根据当前触摸的字母,去自己列表找首字母和触摸字母相同的那个
item,然后让item放置到屏幕顶端(setSelection(position));

c.需要用到获取汉字的拼音,借助类库pinyin4j.jar实现;

QuickIndexBar:

/*
 *  @Copyright (c) tianchenglin
 *  @Author TCL
 *  @Date 2016.6.22
 */

package com.study.tcl.quickindex.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by TCL on 2016/6/22.
 */
public class QuickIndexBar extends View {
    private String[] indexArr = {"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"};
    private Paint mPaint;
    private int mWidth;
    private int mHeight;
    float mSingleLetterHeight;

    private onTouchLetterListener mOnTouchLetterListener;//按住哪个字符的监听

    private int mLastLetterIndex = -1;//最后一次按住的字符

    public QuickIndexBar(Context context) {
        super(context);
        init();

    }

    public QuickIndexBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public QuickIndexBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public void setOnTouchLetterListener(onTouchLetterListener onTouchLetterListener) {
        mOnTouchLetterListener = onTouchLetterListener;
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setTextSize(20);
        mPaint.setTextAlign(Paint.Align.CENTER);//设置文本的起点是文字边框底边的中心
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (int i = 0; i < indexArr.length; i++) {
            float xPosition = mWidth / 2;//要画的x坐标
//            float yPosition = mSingleLetterHeight * i + mSingleLetterHeight;//要画的y坐标
            //y坐标 = 单个字符高度/2+文本高度/2+第几个字符
            float yPosition = mSingleLetterHeight / 2 + mPaint.measureText(indexArr[i], 0, 1) / 2 + i *
                    mSingleLetterHeight;

//            //字符按下变色
//            if (i == mLastLetterIndex) {
//                mPaint.setColor(Color.TRANSPARENT);
//                canvas.drawText(indexArr[i], xPosition, yPosition, mPaint);
//            }
            mPaint.setColor(i == mLastLetterIndex ? Color.WHITE : Color.RED);
            canvas.drawText(indexArr[i], xPosition, yPosition, mPaint);
        }
        super.onDraw(canvas);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_DOWN:
                this.setBackgroundColor(Color.BLUE);

                float y = event.getY();
                int index = (int) (y / mSingleLetterHeight);//得到字符对应的索引
                if (mLastLetterIndex != index) {
                    //安全性检查
                    if (index >= 0 && index < indexArr.length) {
                        if (mOnTouchLetterListener != null) {
                            mOnTouchLetterListener.onTouchLetter(indexArr[index]);
                        }
                    }
                    Log.i("index", index + "");
                    Log.i("letter", indexArr[index]);
                }
                mLastLetterIndex = index;
                break;
            case MotionEvent.ACTION_UP:
                this.setBackgroundColor(Color.WHITE);

                //重置
                mLastLetterIndex = -1;
                break;
        }
        invalidate();//引起重绘
        return true;//自己处理
    }

    public interface onTouchLetterListener {
        void onTouchLetter(String letter);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = getMeasuredWidth();//获取QuickIndexBar宽度
        mHeight = getMeasuredHeight();//获取QuickIndexBar高度
        mSingleLetterHeight = getMeasuredHeight() * 1f / indexArr.length;//获取一个字符所占高度
    }
}

MainActivity:

/*
 *  @Copyright (c) tianchenglin
 *  @Author TCL
 *  @Date 2016.6.22
 */

package com.study.tcl.quickindex;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.ListView;
import android.widget.TextView;

import com.study.tcl.quickindex.adapter.MyListAdapter;
import com.study.tcl.quickindex.domain.Friend;
import com.study.tcl.quickindex.utils.PinYinUtils;
import com.study.tcl.quickindex.view.QuickIndexBar;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MainActivity extends Activity {

    private QuickIndexBar mQuickIndexBar;
    private ListView mListView;
    private List<Friend> friends;
    private TextView mCurrentLetter;

    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        mQuickIndexBar = (QuickIndexBar) findViewById(R.id.quickIndexBar);
        mListView = (ListView) findViewById(R.id.listView);
        mCurrentLetter = (TextView) findViewById(R.id.currentLetter);


        //1.准备数据
        fillList();
        //2.对集合进行排序
        Collections.sort(friends);

        //3.设置adapter
        mListView.setAdapter(new MyListAdapter(this, friends));


        mQuickIndexBar.setOnTouchLetterListener(new QuickIndexBar.onTouchLetterListener() {
            @Override
            public void onTouchLetter(String letter) {
//                Log.e("letter", letter);
                //根据当前触摸的字母,去集合中找哪个item

                for (int i = 0; i < friends.size(); i++) {
                    String firstLetter = friends.get(i).getPinyin().charAt(0) + "";
                    if (letter.equals(firstLetter)) {
                        //将当前item放到屏幕顶端
                        mListView.setSelection(i);
                        break;//找到第一个就行了
                    }
                }

                //显示当前显示的字母
                mCurrentLetter.setVisibility(View.VISIBLE);
                mCurrentLetter.setText(letter);

                //先移除之前的任务
                mHandler.removeCallbacksAndMessages(null);
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mCurrentLetter.setVisibility(View.GONE);
                    }
                }, 1500);
            }
        });

        Log.e("pinyin", PinYinUtils.getPinYin("田成琳"));


    }

    private void fillList() {
        friends = new ArrayList<Friend>();
        // 虚拟数据
        friends.add(new Friend("李伟"));
        friends.add(new Friend("张三"));
        friends.add(new Friend("阿三"));
        friends.add(new Friend("阿四"));
        friends.add(new Friend("段誉"));
        friends.add(new Friend("段正淳"));
        friends.add(new Friend("张三丰"));
        friends.add(new Friend("陈坤"));
        friends.add(new Friend("林俊杰1"));
        friends.add(new Friend("陈坤2"));
        friends.add(new Friend("王二a"));
        friends.add(new Friend("林俊杰a"));
        friends.add(new Friend("张四"));
        friends.add(new Friend("林俊杰"));
        friends.add(new Friend("王二"));
        friends.add(new Friend("王二b"));
        friends.add(new Friend("赵四"));
        friends.add(new Friend("杨坤"));
        friends.add(new Friend("赵子龙"));
        friends.add(new Friend("杨坤1"));
        friends.add(new Friend("李伟1"));
        friends.add(new Friend("宋江"));
        friends.add(new Friend("宋江1"));
        friends.add(new Friend("李伟3"));
    }
}

MyListAdapter:

/*
 *  @Copyright (c) tianchenglin
 *  @Author TCL
 *  @Date 2016.6.23
 */

package com.study.tcl.quickindex.adapter;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.study.tcl.quickindex.R;
import com.study.tcl.quickindex.domain.Friend;
import com.study.tcl.quickindex.utils.PinYinUtils;

import java.util.List;

/**
 * Created by TCL on 2016/6/23.
 */
public class MyListAdapter extends BaseAdapter {
    private List<Friend> mFriends;
    private Context mContext;

    public MyListAdapter(Context context, List<Friend> friends) {
        mFriends = friends;
        mContext = context;
    }

    @Override
    public int getCount() {
        return mFriends.size();
    }

    @Override
    public Object getItem(int position) {
        return mFriends.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = View.inflate(mContext, R.layout.adapter_friend, null);
            holder = new ViewHolder();
            holder.tv_letter = (TextView) convertView.findViewById(R.id.tv_letter);
            holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Friend friend = mFriends.get(position);
        String currentWord = PinYinUtils.getPinYin(friend.getName()).charAt(0) + "";//此处注意异常
        if (position > 0) {
            //获取上一个item首字母
            String lastWord = mFriends.get(position - 1).getPinyin().charAt(0) + "";
            //拿当前的首字母和上一个比较
            if (currentWord.equals(lastWord)) {
                //说明首字母相同
                holder.tv_letter.setVisibility(View.GONE);
            } else {
                holder.tv_letter.setVisibility(View.VISIBLE);
                holder.tv_letter.setText(currentWord);
            }
        } else {
            holder.tv_letter.setVisibility(View.VISIBLE);
            holder.tv_letter.setText(currentWord);
        }
        holder.tv_name.setText(friend.getName());
        return convertView;
    }

    static class ViewHolder {
        TextView tv_letter;
        TextView tv_name;
    }
}
PinyinUtils:

/*
 *  @Copyright (c) tianchenglin
 *  @Author TCL
 *  @Date 2016.6.23
 */

package com.study.tcl.quickindex.utils;

import android.text.TextUtils;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

/**
 * Created by TCL on 2016/6/23.
 */
public class PinYinUtils {

    /**
     * 获取文字拼音,会消耗一定资源,不应该被频繁调用
     *
     * @param chinese
     * @return
     */
    public static String getPinYin(String chinese) {
        if (!TextUtils.isEmpty(chinese)) {
            //1.由于只能对单个汉子进行转化,所以需要将字符串转化成字符数组,然后对每个字符进行转化,最后拼接
            char[] chars = chinese.toCharArray();
            StringBuilder sb = new StringBuilder();
            HanyuPinyinOutputFormat hanyuPinyinOutputFormat = new HanyuPinyinOutputFormat();
            hanyuPinyinOutputFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);//设置转化的拼音是大写字母
            hanyuPinyinOutputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);//没有声调
//            hanyuPinyinOutputFormat.setVCharType();

            for (int i = 0; i < chars.length; i++) {
                //2.过滤空格
                if (Character.isSpaceChar(chars[i])) {
                    continue;
                }
                //3.判断是否是汉字,汉字占两个字节,一个字节范围是-128~127
                if (chars[i] > 127) {
                    //可能是汉字
                    try {
                        String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(chars[i], hanyuPinyinOutputFormat);
                        if (pinyinArray != null) {
                            sb.append(pinyinArray[0]);//取第0个
                        } else {
                            //没有找到拼音,可能不是汉字,则忽略
                        }
                    } catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {
                        badHanyuPinyinOutputFormatCombination.printStackTrace();
                        //说明转化失败,不是汉字,则忽略
                    }
                } else {
                    //这些字符能排序,不能获取拼音
                    sb.append(chars[i]);
                }
            }
            return sb.toString();
        }
        return null;
    }
}

Bean(Friend):

/*
 *  @Copyright (c) tianchenglin
 *  @Author TCL
 *  @Date 2016.6.23
 */

package com.study.tcl.quickindex.domain;

import com.study.tcl.quickindex.utils.PinYinUtils;

/**
 * Created by TCL on 2016/6/23.
 */
public class Friend implements Comparable<Friend> {
    private String name;
    private String pinyin;

    public Friend(String name) {
        this.name = name;

        //一开始就设置好拼音
        setPinyin(PinYinUtils.getPinYin(name));
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPinyin() {
        return pinyin;
    }

    public void setPinyin(String pinyin) {
        this.pinyin = pinyin;
    }

    @Override
    public int compareTo(Friend another) {
        return getPinyin().compareTo(another.getPinyin());
    }
}

activity_main:

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~  @Copyright (c) tianchenglin
  ~  @Author TCL
  ~  @Date 2016.6.22
  -->

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.study.tcl.quickindex.MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </ListView>

    <!--android:background="#B51421"-->
    <com.study.tcl.quickindex.view.QuickIndexBar
        android:id="@+id/quickIndexBar"
        android:layout_width="30dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true">
    </com.study.tcl.quickindex.view.QuickIndexBar>

    <TextView
        android:id="@+id/currentLetter"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:background="@drawable/bg"
        android:gravity="center"
        android:textColor="#ffffff"
        android:visibility="gone"
        android:textSize="50dp"/>

</RelativeLayout>

运行结果:





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值