自定义通讯录索引

来源:互联网 发布:西安app软件开发公司 编辑:程序博客网 时间:2024/05/22 06:05

先看一下效果图吧!
这里写图片描述
我们需求就是在右侧显示字母和“#”,只按顺序显示所列名字拼音的首字母,没使用的不显示,若有名字拼音不是“A-Z”开头的,显示“#”。
如果需求是显示全部26个字母和“#”,或需要触摸显示放大字母,可自行修改。

需要用到的jar包:pinyin4j-2.5.0.jar
下载链接:http://download.csdn.net/detail/qby_nianjun/9845594

还需要用到一个工具类:

package 包名.utils;import android.annotation.SuppressLint;import android.text.TextUtils;import android.util.Log;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.HanyuPinyinVCharType;import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;public class PinYin {    /**     * 讲汉字转换为拼音     *     * @param src     * @return     */    @SuppressLint("DefaultLocale")    public static String getPinYin(String src) {        char[] charArr = null;        if(TextUtils.isEmpty(src)){            return "#";        }        charArr = src.toCharArray();        String[] strArr = new String[charArr.length];        // 设置汉字拼音输出的格式        HanyuPinyinOutputFormat hpOpf = new HanyuPinyinOutputFormat();        hpOpf.setCaseType(HanyuPinyinCaseType.LOWERCASE);        hpOpf.setToneType(HanyuPinyinToneType.WITHOUT_TONE);        hpOpf.setVCharType(HanyuPinyinVCharType.WITH_V);        String str = "";        int t0 = charArr.length;        try {            for (int i = 0; i < t0; i++) {                // 判断是否为汉字字符                if (Character.toString(charArr[i])                        .matches("[\\u4E00-\\u9FA5]+")) {                    strArr = PinyinHelper.toHanyuPinyinStringArray(charArr[i],                            hpOpf);// 将汉字的几种全拼都存到t2数组中                    str += strArr[0];// 取出该汉字全拼的第一种读音并连接到字符串t4后                } else {                    // 如果不是汉字字符,直接取出字符并连接到字符串t4后                    str += Character.toString(charArr[i]);                }            }        } catch (BadHanyuPinyinOutputFormatCombination e) {            Log.e("getPinYin error.", e.toString());        }        return str;    }    /**     * 将汉字转换为拼音--首字母     *     * @param str     * @return     */    public static String getPinYinHeadChar(String str) {        String convert = "";        if(TextUtils.isEmpty(str)){            return "#";        }        for (int j = 0; j < str.length(); j++) {            char word = str.charAt(j);            // 提取汉字的首字母            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(word);            if (pinyinArray != null) {                convert += pinyinArray[0].charAt(0);            } else {                convert += word;            }        }        return convert;    }    /**     * 讲汉字转换为字节序列     *     * @param cnStr     * @return     */    public static String getCnASCII(String cnStr) {        StringBuffer strBuf = new StringBuffer();        // 将字符串转换成字节序列        byte[] bGBK = cnStr.getBytes();        for (int i = 0; i < bGBK.length; i++) {            strBuf.append(Integer.toHexString(bGBK[i] & 0xff));        }        return strBuf.toString();    }}

然后是自定义索引的控件:

package 包名.view;/** * Created by qby on 2017/2/20 0020. */import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.support.v7.widget.AppCompatTextView;import android.util.AttributeSet;import android.view.MotionEvent;import 包名.R;public class SideBar extends AppCompatTextView {    private String[] letters = new String[]{"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 textPaint;//    private Paint bigTextPaint;//    private Paint scaleTextPaint;    private Canvas canvas;    private int itemH;    private int w;    private int h;    /**     * 普通情况下字体大小     */    float singleTextH;    /**     * 缩放离原始的宽度     */    private float scaleWidth;    /**     * 滑动的Y     */    private float eventY = 0;    /**     * 缩放的倍数     */    private int scaleSize = 1;    /**     * 缩放个数item,即开口大小     */    private int scaleItemCount = 0;    private ISideBarSelectCallBack callBack;    public SideBar(Context context) {        this(context, null);    }    public SideBar(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(attrs);    }    private void init(AttributeSet attrs) {        if (attrs != null) {            TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);            scaleSize = ta.getInteger(R.styleable.SideBar_scaleSize, 1);            scaleItemCount = ta.getInteger(R.styleable.SideBar_scaleItemCount, 6);            scaleWidth = ta.getDimensionPixelSize(R.styleable.SideBar_scaleWidth, dp(100));            ta.recycle();        }        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        textPaint.setColor(Color.GRAY);        textPaint.setTextSize(getTextSize());        textPaint.setTextAlign(Paint.Align.CENTER);//        bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//        bigTextPaint.setColor(Color.GREEN);//        bigTextPaint.setTextSize(getTextSize() * (scaleSize + 3));//        bigTextPaint.setTextAlign(Paint.Align.CENTER);//        scaleTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//        scaleTextPaint.setColor(Color.GRAY);//        scaleTextPaint.setTextSize(getTextSize() * (scaleSize + 1));//        scaleTextPaint.setTextAlign(Paint.Align.CENTER);    }    public void setDataResource(String[] data) {        letters = data;        invalidate();    }    public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {        this.callBack = callBack;    }    /**     * 设置字体缩放比例     *     * @param scale     */    public void setScaleSize(int scale) {        scaleSize = scale;        invalidate();    }    /**     * 设置缩放字体的个数,即开口大小     *     * @param scaleItemCount     */    public void setScaleItemCount(int scaleItemCount) {        this.scaleItemCount = scaleItemCount;        invalidate();    }    private int dp(int px) {        final float scale = getContext().getResources().getDisplayMetrics().density;        return (int) (px * scale + 0.5f);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:            case MotionEvent.ACTION_MOVE:                if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {                    eventY = event.getY();                    invalidate();                    return true;                } else {                    eventY = 0;                    invalidate();                    break;                }            case MotionEvent.ACTION_CANCEL:                eventY = 0;                invalidate();                return true;            case MotionEvent.ACTION_UP:                if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {                    eventY = 0;                    invalidate();                    return true;                } else                    break;        }        return super.onTouchEvent(event);    }    @Override    protected void onDraw(Canvas canvas) {        this.canvas = canvas;        DrawView(eventY);    }    private void DrawView(float y) {        int currentSelectIndex = -1;        if (y != 0) {            for (int i = 0; i < letters.length; i++) {                float currentItemY = itemH * i + itemH * (27 - letters.length)/2;                float nextItemY = itemH * (i + 1) + itemH * (27 - letters.length)/2;                if (y >= currentItemY && y < nextItemY) {                    currentSelectIndex = i;                    if (callBack != null) {                        callBack.onSelectStr(currentSelectIndex, letters[i]);                    }                    //画大的字母//                    Paint.FontMetrics fontMetrics = bigTextPaint.getFontMetrics();//                    float bigTextSize = fontMetrics.descent - fontMetrics.ascent;//                    canvas.drawText(letters[i], w - getPaddingRight() - scaleWidth - bigTextSize, singleTextH + itemH * i + itemH * (27 - letters.length)/2, bigTextPaint);                }            }        }        drawLetters(y, currentSelectIndex);    }    private void drawLetters(float y, int index) {        //第一次进来没有缩放情况,默认画原图        if (index == -1) {            w = getMeasuredWidth();            h = getMeasuredHeight();            itemH = h / 27;            Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();            singleTextH = fontMetrics.descent - fontMetrics.ascent;            for (int i = 0; i < letters.length; i++) {                canvas.drawText(letters[i], w - getPaddingEnd(), singleTextH + itemH * i  + itemH * (27 - letters.length)/2, textPaint);            }            //触摸的时候画缩放图        } else {            //遍历所有字母            for (int i = 0; i < letters.length; i++) {                //要画的字母的起始Y坐标                float currentItemToDrawY = singleTextH + itemH * i  + itemH * (27 - letters.length)/2;                float centerItemToDrawY;                if (index < i)                    centerItemToDrawY = singleTextH + itemH * (index + scaleItemCount) + itemH * (27 - letters.length)/2;                else                    centerItemToDrawY = singleTextH + itemH * (index - scaleItemCount) + itemH * (27 - letters.length)/2;                float delta = 1 - Math.abs((y - currentItemToDrawY) / (centerItemToDrawY - currentItemToDrawY));                float maxRightX = w - getPaddingRight();                //如果大于0,表明在y坐标上方//                scaleTextPaint.setTextSize(getTextSize() + getTextSize() * delta);                float drawX = maxRightX - scaleWidth * delta;                //超出边界直接花在边界上//                if (drawX > maxRightX)                    canvas.drawText(letters[i], maxRightX, singleTextH + itemH * i  + itemH * (27 - letters.length)/2, textPaint);//                else//                    canvas.drawText(letters[i], drawX, singleTextH + itemH * i  + itemH * (27 - letters.length)/2, scaleTextPaint);            }        }    }    public interface ISideBarSelectCallBack {        void onSelectStr(int index, String selectStr);    }}

准备完毕,使用SideBar时只需要在布局中加入该控件:

<RelativeLayout        android:id="@+id/rl"        android:layout_width="match_parent"        android:layout_height="match_parent">        <ListView            android:id="@+id/listView"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:layout_marginBottom="10dp"            android:layout_marginTop="15dp" />        <包名.view.SideBar            android:id="@+id/sideBar"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:layout_alignParentEnd="true"            android:layout_centerVertical="true"            android:paddingEnd="10dp" />    </RelativeLayout>

想要让字母按A-Z#排序,需要对获取的从后台获取的JavaBean实现Comparable:

package 包名.bean;import 包名.utils.PinYin;import java.util.List;/** * Created by qby on 2016/8/12 0012. */public class FansListBean {    public int status;    public String message;    /**     * id : 7     * user_name : j     * phone : 120     * user_type : 1     * contact_user_id : 1     * user_id : 27     * avator : null     */    public List<DataBean> data;    public static class DataBean implements Comparable<DataBean>{        private String name; // 姓名        private String pinyin; // 姓名对应的拼音        private String firstLetter; // 拼音的首字母        public int id;        public String user_name;        public int user_type;        public int contact_user_id;        public String avator;        public DataBean(){        }        public DataBean(int id,String name,int user_type,int contact_user_id,String avator) {            this.id = id;            this.name = name;            this.user_type = user_type;            this.contact_user_id = contact_user_id;            this.avator = avator;            pinyin = PinYin.getPinYin(name);            firstLetter = String.valueOf(PinYin.getPinYinHeadChar(name).charAt(0)).toUpperCase(); // 获取拼音首字母并转成大写            if (!firstLetter.matches("[A-Z]")) { // 如果不在A-Z中则默认为“#”                firstLetter = "#";            }        }        public String getName() {            return name;        }        public String getPinyin() {            return pinyin;        }        public String getFirstLetter() {            return firstLetter;        }        @Override        public int compareTo(DataBean another) {            if (firstLetter.equals("#") && !another.getFirstLetter().equals("#")) {                return 1;            } else if (!firstLetter.equals("#") && another.getFirstLetter().equals("#")){                return -1;            } else {                return pinyin.compareToIgnoreCase(another.getPinyin());            }        }    }}

列表条目布局: lv_contacts_item

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/white"    android:orientation="vertical">    <LinearLayout        android:id="@+id/ll_tip"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@color/wheel_white"        android:orientation="vertical">        <TextView            android:id="@+id/tv_tip"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginBottom="5dp"            android:layout_marginEnd="25dp"            android:layout_marginStart="25dp"            android:layout_marginTop="5dp"            android:text="A"            android:textColor="#888"            android:textSize="14dp" />        <View            android:layout_width="match_parent"            android:layout_height="0.5dp"            android:background="@color/lightwhite" />    </LinearLayout>    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:paddingBottom="10dp"        android:paddingEnd="15dp"        android:paddingStart="15dp"        android:paddingTop="10dp"        >        <com.duola.duolainter.duolainter.view.CircleImageView            android:id="@+id/iv_user"            android:layout_width="50dp"            android:layout_height="50dp"            android:layout_centerVertical="true"            android:layout_marginStart="5dp"            android:src="@mipmap/icon_user_photo_default" />        <LinearLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerVertical="true"            android:layout_marginStart="10dp"            android:layout_toEndOf="@+id/iv_user"            android:orientation="vertical">            <TextView                android:id="@+id/tv_name"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="阿拉蕾"                android:textColor="@color/black"                android:textSize="16dp" />            <TextView                android:id="@+id/tv_user_type"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginTop="5dp"                android:text="车主" />        </LinearLayout>    </RelativeLayout></LinearLayout>

下面就是在页面中的使用了:

private ArrayList<FansListBean.DataBean> datas = new ArrayList<FansListBean.DataBean>();//声明一个集合private ArrayAdapter<FansListBean.DataBean> adapter;//声明适配器声明控件:private SideBar sideBar;初始化控件:sideBar = (SideBar) findViewById(R.id.sideBar);sideBar.setOnStrSelectCallBack(new SideBar.ISideBarSelectCallBack() {            @Override            public void onSelectStr(int index, String selectStr) {                for (int i = 0; i < datas.size(); i++) {                    if (selectStr.equalsIgnoreCase(datas.get(i).getFirstLetter())) {                        listView.setSelection(i); // 选择到首字母出现的位置                        return;                    }                }            }        });设置adapter:adapter = new ArrayAdapter<FansListBean.DataBean>(ctx, R.layout.lv_contacts_item, datas) {            /**             * 获取catalog首次出现位置             */            public int getPositionForSection(String catalog) {                for (int i = 0; i < getCount(); i++) {                    String sortStr = datas.get(i).getFirstLetter();                    if (catalog.equalsIgnoreCase(sortStr)) {                        return i;                    }                }                return -1;            }            @Override            public View getView(final int position, View convertView, ViewGroup parent) {                ViewHolder holder;                if (convertView == null) {                    convertView = View.inflate(ctx, R.layout.lv_contacts_item, null);                    holder = new ViewHolder(convertView);                    convertView.setTag(holder);                } else {                    holder = (ViewHolder) convertView.getTag();                }                FansListBean.DataBean dataBean = datas.get(position);                //根据position获取首字母作为目录catalog                String catalog = datas.get(position).getFirstLetter();                //如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现                if (position == getPositionForSection(catalog)) {                    holder.ll_tip.setVisibility(View.VISIBLE);                    holder.tv_tip.setText(catalog.toUpperCase());                } else {                    holder.ll_tip.setVisibility(View.GONE);                }                if (!TextUtils.isEmpty(dataBean.avator)) {//设置网络图片                   Picasso.with(ctx).load(dataBean.avator).error(R.mipmap.icon_user_photo_default).resize(Tool.dip2px(ctx, 50), Tool.dip2px(ctx, 50)).into(holder.iv_user);                } else {//设置默认图片                    Picasso.with(ctx).load(R.mipmap.icon_user_photo_default).resize(Tool.dip2px(ctx, 50), Tool.dip2px(ctx, 50)).into(holder.iv_user);                }                if (dataBean.user_type == 0) {                    holder.tv_user_type.setText("货主");                } else if (dataBean.user_type == 1) {                    holder.tv_user_type.setText("车主");                }                holder.tv_name.setText(dataBean.getName());                return convertView;            }        };        listView.setAdapter(adapter);请求回调中:FansListBean fansListBean = new Gson().fromJson(s, FansListBean.class);int status = fansListBean.status;if (status == 1) {   datas.clear();   List<FansListBean.DataBean> data = fansListBean.data;   if (data.size() == 0) {       rl.setVisibility(View.GONE);   } else {       rl.setVisibility(View.VISIBLE);       for (FansListBean.DataBean bean : data) {           if (!TextUtils.isEmpty(bean.user_name)) {               datas.add(new FansListBean.DataBean(bean.id, bean.user_name, bean.user_type, bean.contact_user_id, bean.avator));           }       }       Collections.sort(datas); // 对list进行排序,需要让User实现Comparable接口重写compareTo方法       adapter.notifyDataSetChanged();       ArrayList<String> al = new ArrayList<String>();       for (FansListBean.DataBean bean : datas) {           if (!al.contains(bean.getFirstLetter())) {               al.add(bean.getFirstLetter().toUpperCase());           }       }       indexs = al.toArray(new String[al.size()]);       sideBar.setDataResource(indexs);//把要显示的字母传给SideBar   }} else {   ToastUtil.showTextToast(ctx, fansListBean.message);}
原创粉丝点击