自己研究了好几天联系人的T9搜索算法, 先分享出来给大家看看. 欢迎不吝赐教.假设有大神有更好的T9搜索算法, 那更好啊,大家一起研究研究,谢谢.

第一部分是比較简单的获取手机联系人.

获取联系人前提要有权限.

<uses-permission android:name="android.permission.READ_CONTACTS" />

由于手机的联系人都存储在数据库里面,所以我们仅仅要把数据库里的信息查询出来就可以.

private static final String[] PHONES_PROJECTION = new String[] {
Phone.DISPLAY_NAME, Phone.NUMBER, Photo.SORT_KEY_ALTERNATIVE};

ContentResolver resolver = getBaseContext().getContentResolver();
Cursor phoneCursor = resolver.query(Phone.CONTENT_URI,
PHONES_PROJECTION, null, null, null);

这里我仅仅是简单的说一下而已,后面的遍历phoneCursor 就能把联系人查询出来.(我项目里是开一条线程获取联系人的,以防太多联系人导致页面空白或者卡顿)

第二部分是T9搜索部分

思路是: a仅仅要联系人的号码有包括我输入的数字就add到list进去,

b联系人的姓名拼音有包括我输入的拼音就add到list进去,

c联系人的姓名缩写拼音有包括我输入的拼音就add到list进去

举个样例--联系人姓名:測试,号码:1234567890

情况一:我输入1234567890就能把測试显示出来

情况二:我输入23744(ceshi),由于这个"測试"的拼音,所以也能把測试显示出来

情况三:我输入(27)cs,这是"測试"的拼音缩写,所以也能把測试显示出来

当然我输入23(ce)或者744(shi)什么的,都能够把測试显示出来.

个人是把联系人的号码(1234567890),姓名拼音转成数字(ceshi相应是23744),拼音缩写拼音转成数字(cs相应27),这些信息存储到list里面.便于遍历跟比較.

然后再结合代码解说一下:

我的流程是这种:

首先開始发送一条线程去获取联系人

/**
* 刚開始启动程序时,开条线程去获取联系人
*/
new Thread(getContract).start();

这线程里面获取到的联系人存储到mLIst里面;

当中在获取的时候处理了一下,把联系人姓名,号码,拼音,拼音缩写查出来后放到bean里面;

private static final String[] PHONES_PROJECTION = new String[] {
Phone.DISPLAY_NAME, Phone.NUMBER, Photo.SORT_KEY_ALTERNATIVE};

这个是查出联系人的姓名,号码,姓名拼音:測试,1234567890,CE 測 SHI 试

(对Contacts了解就会知道(不了解能够了解下),每一个联系人都有一个sort_key字段,假设查询中没有设置sortOrder,默认就会以 sort_key字段为排序依据.名字的检索事实上也是依据sort_key来做的(比方拨号盘的模糊匹配:数字转成字母。再到拼音,最后得到汉字).sort_key是依据名字生成的:假设联系人名字中包括字母。sort_key和name保持一致;假设名字是汉字,生成的sort_key,"拼音 汉字-拼音 汉字".当中拼音全大写,中间以空格切割,如:"測试"相应的sort_key:"CE
測 SHI 试")

外语:格式就是以上那样,由于Google有提供一个汉字转拼音的类,然后把"CE 測 SHI 试"存储到数据库里.所以用这个就能查出来.但毕竟是外国人弄的,所以在拼音上有时候会有一些差池.比如咱们常见的"呵呵",咱们习惯是"HE 呵 HE 呵",但它可能会保存成"A 呵 A 呵".

/**
* 将联系人的姓名拼音所有转化为数字
* @param 联系人姓名拼音
* @return 姓名拼音相应数字
*/
public String getNum(String search, boolean status){
String str = "";
for(int i = 0;i<search.length();i++){
String c = search.charAt(i)+"";
if(c.equals("1")){
str = str + "1";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("A")||c.equals("B")||c.equals("C")||c.equals("2")
||c.equals("a")||c.equals("b")||c.equals("c")){
str = str + "2";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("D")||c.equals("E")||c.equals("F")||c.equals("3")
||c.equals("d")||c.equals("e")||c.equals("f")){
str = str + "3";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("G")||c.equals("H")||c.equals("I")||c.equals("4")
||c.equals("g")||c.equals("h")||c.equals("i")){
str = str + "4";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("J")||c.equals("K")||c.equals("L")||c.equals("5")
||c.equals("j")||c.equals("k")||c.equals("l")){
str = str + "5";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("M")||c.equals("N")||c.equals("O")||c.equals("6")
||c.equals("m")||c.equals("n")||c.equals("o")){
str = str + "6";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("P")||c.equals("Q")||c.equals("R")||c.equals("S")||c.equals("7")
||c.equals("p")||c.equals("q")||c.equals("r")||c.equals("s")){
str = str + "7";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("T")||c.equals("U")||c.equals("V")||c.equals("8")
||c.equals("t")||c.equals("u")||c.equals("v")){
str = str + "8";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("W")||c.equals("X")||c.equals("Y")||c.equals("Z")||c.equals("9")
||c.equals("w")||c.equals("x")||c.equals("y")||c.equals("z")){
str = str + "9";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("0")){
str = str + "0";
if(status){
i = i + 1;
}
continue;
}
}
return str;
};

这种方法主要是将拼音转换成数字,比如測试的"ce 測 shi 试"就能转成23744.大家预计还看到我还传了个參数boolean status,这个是用于判读是否为拼音字母缩写的,假设为true的话,他会查出測试的"ce
測 shi 试"的字母缩写,cs转成27.

获取到联系人,再显示出来之后,就是搜索了.

整个的事件监听关键在于edittext的变化监听

inputEditText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
if (isWrite) {
isWrite = false;
return;
}
isWrite = true;
String inputStr = "";
String newStr = s.toString();
newStr = newStr.replace(" ", "");
int index = 0;
if (true) {
if ((index + 3) < newStr.length()) {
inputStr += (newStr.substring(index, index + 3) + " ");
index += 3;
}
}
while ((index + 4) < newStr.length()) {
inputStr += (newStr.substring(index, index + 4) + " ");
index += 4;
}
inputStr += (newStr.substring(index, newStr.length()));
inputEditText.setText(inputStr);
inputEditText.setSelection(inputStr.length()); if(count == 0){
isAfresh = true;
}else{
isAfresh = false;
} long currentTime = System.currentTimeMillis();
if ((currentTime - touchTime) >= waitTime) {
touchTime = currentTime;
searchConstract(isAfresh);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
@Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});

前面一段是为了格式化输入格式.后面调用的方法才是做相应的操作.直接依据用户输入的数字,推断联系人的姓名拼音,姓名缩写,号码有没有包括用户输入的数字,有的话就add到list里面.遍历完整个联系人之后,就刷新adapter.显示结果出来.

各位假设认为easy看得懂,或者自己能用到的,能不能在页面底下投一下您尊贵的一票呢,谢谢!

以上是我的思路,以下附上一段代码

public class MainT9 extends Activity{

	private int[] textViewId = new int[]{R.id.main_num_1,R.id.main_num_2,R.id.main_num_3,R.id.main_num_4,
R.id.main_num_5,R.id.main_num_6,R.id.main_num_7,R.id.main_num_8,R.id.main_num_9,R.id.main_num_left,
R.id.main_num_0,R.id.main_num_right,R.id.main_num_delete};
private TextView[] textView = new TextView[textViewId.length];
private EditText inputEditText; private ListView mListView; private List<ContractBean> mList;
private List<ContractBean> list;
private MyAdapter myAdapter; private static final String[] PHONES_PROJECTION = new String[] {
Phone.DISPLAY_NAME, Phone.NUMBER, Photo.SORT_KEY_ALTERNATIVE}; long waitTime = 300;
long touchTime = 0; /**
* 推断是否人为输入
*/
private boolean isWrite = false;
private boolean isAfresh = false; @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
this.setContentView(R.layout.main_activity); mList = new ArrayList<ContractBean>();
list = new ArrayList<ContractBean>(); /**
* 刚開始启动程序时,开条线程去获取联系人
*/
new Thread(getContract).start(); for(int i = 0;i<textViewId.length;i++){
textView[i] = (TextView) this.findViewById(textViewId[i]);
textView[i].setOnClickListener(click);
} inputEditText = (EditText) this.findViewById(R.id.main_num_edit);
// hideSystemKeyBoard(inputEditText); mListView = (ListView) this.findViewById(R.id.main_contract_listview);
myAdapter = new MyAdapter(this, mList);
mListView.setAdapter(myAdapter); inputEditText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
if (isWrite) {
isWrite = false;
return;
}
isWrite = true;
String inputStr = "";
String newStr = s.toString();
newStr = newStr.replace(" ", "");
int index = 0;
if (true) {
if ((index + 3) < newStr.length()) {
inputStr += (newStr.substring(index, index + 3) + " ");
index += 3;
}
}
while ((index + 4) < newStr.length()) {
inputStr += (newStr.substring(index, index + 4) + " ");
index += 4;
}
inputStr += (newStr.substring(index, newStr.length()));
inputEditText.setText(inputStr);
inputEditText.setSelection(inputStr.length()); if(count == 0){
isAfresh = true;
}else{
isAfresh = false;
} long currentTime = System.currentTimeMillis();
if ((currentTime - touchTime) >= waitTime) {
touchTime = currentTime;
searchConstract(isAfresh);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
@Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
} private void searchConstract(boolean isAfresh){
if(isAfresh){
if(mList!=null){
myAdapter.updateListView(mList);
}else{
new Thread(getContract).start();
} }else{
String str1= inputEditText.getText().toString();
str1 = str1.replace(" ", ""); SearchContract search = new SearchContract(str1);
search.start();
}
} /**
* T9搜索线程
* @author Chillax_KUN
*/
class SearchContract extends Thread{
String body;
SearchContract(String body){
this.body = body;
} @Override
public void run() {
// TODO Auto-generated method stub
super.run();
Iterator<ContractBean> iterator = mList.iterator();
list = new ArrayList<ContractBean>();
list.clear(); while(iterator.hasNext()){
ContractBean sortModel = iterator.next();
String search = sortModel.getSearch();
String phone = sortModel.getPhone();
phone = phone.replace(" ", "");
String zimu = sortModel.getZimu(); if(phone.contains(body) || search.contains(body)||zimu.contains(body)){
list.add(sortModel);
}
}
h.sendEmptyMessage(1);
} } /**
* 开启线程,获取联系人
*/
Runnable getContract = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mList = getPhoneContacts();
h.sendEmptyMessage(0);
}
}; private List<ContractBean> getPhoneContacts() {
ContentResolver resolver = getBaseContext().getContentResolver();
Cursor phoneCursor = resolver.query(Phone.CONTENT_URI,
PHONES_PROJECTION, null, null, null); List<ContractBean> mList = new ArrayList<ContractBean>(); if (phoneCursor != null) {
phoneCursor.moveToFirst();
while (!phoneCursor.isAfterLast()) {
ContractBean model = new ContractBean(); String name = phoneCursor.getString(0);
String phone = phoneCursor.getString(1);
String search = phoneCursor.getString(2); model.setName(name);
model.setPhone(phone);
model.setSearch(getNum(search,false)); String zimu = "";
String str[] = search.split(" ");
if(str.length>0){
for(int i = 0;i<str.length;i++){
if(str[i].length()==1){
zimu = zimu + str[i];
}
}
} model.setZimu(getNum(zimu,true)); mList.add(model);
phoneCursor.moveToNext();
}
if(phoneCursor!=null){
phoneCursor.close();
}
return mList;
}
return mList;
} /**
* 将联系人的姓名拼音所有转化为数字
* @param 联系人姓名拼音
* @return 姓名拼音相应数字
*/
public String getNum(String search, boolean status){
String str = "";
for(int i = 0;i<search.length();i++){
String c = search.charAt(i)+"";
if(c.equals("1")){
str = str + "1";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("A")||c.equals("B")||c.equals("C")||c.equals("2")
||c.equals("a")||c.equals("b")||c.equals("c")){
str = str + "2";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("D")||c.equals("E")||c.equals("F")||c.equals("3")
||c.equals("d")||c.equals("e")||c.equals("f")){
str = str + "3";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("G")||c.equals("H")||c.equals("I")||c.equals("4")
||c.equals("g")||c.equals("h")||c.equals("i")){
str = str + "4";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("J")||c.equals("K")||c.equals("L")||c.equals("5")
||c.equals("j")||c.equals("k")||c.equals("l")){
str = str + "5";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("M")||c.equals("N")||c.equals("O")||c.equals("6")
||c.equals("m")||c.equals("n")||c.equals("o")){
str = str + "6";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("P")||c.equals("Q")||c.equals("R")||c.equals("S")||c.equals("7")
||c.equals("p")||c.equals("q")||c.equals("r")||c.equals("s")){
str = str + "7";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("T")||c.equals("U")||c.equals("V")||c.equals("8")
||c.equals("t")||c.equals("u")||c.equals("v")){
str = str + "8";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("W")||c.equals("X")||c.equals("Y")||c.equals("Z")||c.equals("9")
||c.equals("w")||c.equals("x")||c.equals("y")||c.equals("z")){
str = str + "9";
if(status){
i = i + 1;
}
continue;
}else if(c.equals("0")){
str = str + "0";
if(status){
i = i + 1;
}
continue;
}
}
return str;
}; Handler h = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(msg.what==0){
if(mList!=null && myAdapter!=null){
myAdapter.updateListView(mList);
myAdapter.notifyDataSetChanged();
}
} else if(msg.what==1){
myAdapter.updateListView(list);
} }
}; OnClickListener click = new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.main_num_1:
inputEditText.setText(inputEditText.getText()+"1");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_2:
inputEditText.setText(inputEditText.getText()+"2");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_3:
inputEditText.setText(inputEditText.getText()+"3");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_4:
inputEditText.setText(inputEditText.getText()+"4");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_5:
inputEditText.setText(inputEditText.getText()+"5");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_6:
inputEditText.setText(inputEditText.getText()+"6");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_7:
inputEditText.setText(inputEditText.getText()+"7");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_8:
inputEditText.setText(inputEditText.getText()+"8");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_9:
inputEditText.setText(inputEditText.getText()+"9");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_0:
inputEditText.setText(inputEditText.getText()+"0");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_left:
inputEditText.setText(inputEditText.getText()+"*");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_right:
inputEditText.setText(inputEditText.getText()+"#");
inputEditText.setSelection(inputEditText.getText().toString().length());
break;
case R.id.main_num_delete:
String string = inputEditText.getText().toString();
if(!string.equals("")){
inputEditText.setText(string.subSequence(0, string.length()-1));
inputEditText.setSelection(inputEditText.getText().toString().length());
}
break;
default:
break;
}
}
}; /**
* 通过反射调用setShowSoftInputOnFocus(false) 来隐藏键盘。 用 InputType.TYPE_NULL方法,无法显示光标。
*/
private void hideSystemKeyBoard(View view) {
this.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
try {
Class<EditText> cls = EditText.class;
Method setSoftInputShownOnFocus;
// 此方法为隐藏的需用java反射调用
setSoftInputShownOnFocus = cls.getMethod("setShowSoftInputOnFocus",
boolean.class);
setSoftInputShownOnFocus.setAccessible(true);
setSoftInputShownOnFocus.invoke((EditText) view, false);
} catch (Exception e) {
e.printStackTrace();
}
}
}

源代码地址:(临时被我删除了,由于好像上传错了demo,晚上回去改哈)

个人认为以上的算法还不是最高效的,假设有更好的算法,麻烦指点指点.

尊重原创, 转载请注明出处:http://blog.csdn.net/chillax_li/article/details/29380615

玩爆你的手机联系人--T9搜索(一)的更多相关文章

  1. 你真的会玩SQL吗?玩爆你的数据报表之存储过程编写(上)

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  2. Android-AsyncTask异步任务(获取手机联系人)

    本篇随笔将讲解一下Android的多线程的知识,以及如何通过AsyncTask机制来实现线程之间的通信. 一.Android当中的多线程 在Android当中,当一个应用程序的组件启动的时候,并且没有 ...

  3. android131 360 05 手势触摸滑动,sim卡,开机启动的广播,手机联系人,SharedPreferences,拦截短信

    安卓手势触摸滑动: package com.itheima52.mobilesafe.activity; import android.app.Activity; import android.con ...

  4. 手势识别=读取手机联系人=ContentResolver-Day3

    手势识别=读取手机联系人=ContentResolverDay32 mobile3.0 手机设置向导页面完成 选择器没有做完成 样式提取完成 自定义控件的优化继续 抽取父类Activity 完成 手机 ...

  5. android利用ContentResolver访问者获取手机联系人信息

    转载自:http://www.jb51.net/article/106379.htm 首先需要在AndroidManifest.xml文件中添加权限: <uses-permission andr ...

  6. Expo大作战(三十九)--expo sdk api之 DocumentPicker,Contacts(获取手机联系人信息),Branch

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  7. 记录一次手机联系人整理(XML文件格式处理)

    场景:1.IOS手机和Android手机联系人同步时有部分重复联系人. 2.很早以前的HTC手机导出的联系人中备注信息有大量乱码,且很多联系人生日被设置为1970-01-01,导致生日提醒软件产生骚扰 ...

  8. 读取手机联系人,并用listview显示

    读取手机联系人,用到的就是一个contentprovider. 数据库里面有三张重要的表 raw_contact 里面有所有联系人的数据 data 每个联系人的所有数据 mime-type 每条数据的 ...

  9. Android 读取手机联系人、拨号、发送短信及长按菜单的操作

    本示例实现了读取手机联系人,拨号.发送短信及长按出现菜单选项的操作↓ 1.Andrid项目结构图↓主要操作图中红色方框内的文件. 2.首先布局代码如下↓ a, main.xml 程序运行的主界面,主要 ...

随机推荐

  1. 按字节读取txt文件缓存区大小设置多少比较好?

    读取 txt 文件常规写法有逐行读取和按照字节缓存读取,那么按照字节缓存读取时,设置缓存区多大比较好呢?百度了一下,没发现有说这个问题的,自测了一把,以事实说话. 常规读取方法如下: // 字节流读取 ...

  2. CentOS6.5系统解决中文乱码问题

      一.    问题详情 Windows的默认编码为GBK,Linux的默认编码为UTF-8.在Windows下编辑的中文,在Linux下显示为乱码.为了解决此问题,修改Linux的默认编码为GBK. ...

  3. 05.Linux-CentOS系统本地Yum源搭建

    CentOS系统 1.挂载镜像光盘[root@localhost ~]# mount /dev/sr0 /media/cdrom/ 2.创建本地yum源仓库[root@localhost ~]# cd ...

  4. Java虚拟机——Class类文件结构

    Class文件格式采用一种类似C语言结构体的结构来存储数据,这种数据结构只有两种数据类型:无符号数和表.      无符号数属于基本的数据类型,数据项的不同长度分别用u1, u2, u4, u8表示, ...

  5. 吉首大学2019年程序设计竞赛(重现赛)I 滑稽树上滑稽果 (莫队+逆元打表)

    链接:https://ac.nowcoder.com/acm/contest/992/I来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒空间限制:C/C++ 32768K,其他语言65536K  ...

  6. jQuery入门、jQuery选择器、jQuery操作

    一.什么是jQuery及如何使用 1.1 jQuery 简介 jQuery是一个兼容多浏览器的javascript函数库(把我们常用的一些功能进行了封装,方便我们来调用,提高我们的开发效率.),核心理 ...

  7. python数据分析第二版:numpy

    一:Numpy # 数组和列表的效率问题,谁优谁劣 # 1.循环遍历 import numpy as np import time my_arr = np.arange(1000000) my_lis ...

  8. mysql 5.6 datetime default now()

    CREATE TABLE `test` (   id int,   `gmt_create` datetime  DEFAULT NOW() not NULL )ENGINE=InnoDB; mysq ...

  9. cocos2dx-Lua3.10版本使用cjson

    参考:https://blog.csdn.net/shimazhuge/article/details/79848199 1.首先将cjson加入到libluacocos2d工程(cjson目录:/f ...

  10. POJ 3259 Wormholes ( SPFA判断负环 && 思维 )

    题意 : 给出 N 个点,以及 M 条双向路,每一条路的权值代表你在这条路上到达终点需要那么时间,接下来给出 W 个虫洞,虫洞给出的形式为 A B C 代表能将你从 A 送到 B 点,并且回到 C 个 ...