自定义通讯录索引
来源:互联网 发布:西安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);}
阅读全文
0 0
- 自定义通讯录字母索引
- 自定义通讯录索引
- Android 自定义 View 实现通讯录字母索引(仿微信通讯录)
- Android 自定义 View 实现通讯录字母索引(仿微信通讯录)
- android自定义View----通讯录导航栏(快速查找索引)
- android 通讯录+首字母索引
- 通讯录 之 快速索引
- 通讯录索引SideBarView
- 读通讯录源码。学会了列表索引,自定义滑块等、、
- [Android实例] 读通讯录源码。学会了列表索引,自定义滑块等
- Andorid使用自定义View实现通讯录,媒体文件等等的ListView的字母索引
- iOS 自定义通讯录(包含姓名和电话等 按拼音分组,索引)
- Android自定义控件5----继承View通讯录索引功能之1初始化显示字母列表
- 自定义cell 通讯录
- 自定义iOS通讯录
- UITableView 类似通讯录的索引
- 通讯录侧滑索引菜单
- AddressBook通讯录右边索引条
- SimpleJSON在unity端序列化和反序列化
- oracle时间函数(三)last_day(),add_months(),month between()
- Schema简单教程
- boost库之udp client实例
- c++中“箭头(->)”和“点号(.)”操作符的区别
- 自定义通讯录索引
- rmi远程接口调用示例
- DNS在架构设计中的巧用
- JVM内存结构、垃圾回收那点事
- Spring实战学习(四)
- 延迟队列DelayQueue示例
- Align text around ImageSpan center vertical bug修复
- 记自定义弹框dialog
- 欢迎使用CSDN-markdown编辑器