自己研究了好几天联系人的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. 修改 linux 默认字符集

    [root@eric6 ~]# cat /etc/sysconfig/i18n //查看 linux 默认的字符集,默认是 UTF-8 LANG="zh_CN.UTF-8" cp ...

  2. 高精乘(fft板子

    哇..fft的原理真的是不太好懂,看了好久许多细节还是不太清楚,但感觉本质就是用了单位根的性质. https://www.luogu.org/problem/P1919 #include<cst ...

  3. css3背景颜色渐变属性

    https://www.cnblogs.com/ningkyolei/p/4623697.html 很久之前写的一篇文章了,今天重新整理一下关于css3背景渐变的写法,至于是怎么来的,可以看下面渐变的 ...

  4. windows cmd bat处理文件

    bat中输入: @echo offtitle 正在承载无线网络....netsh wlan start hostednetworknetsh wlan show hostednetworkecho 启 ...

  5. spring mvc 和spring boot 中注解的使用

    1 spring mvc和spring boot之间的关系 spring boot包含spring mvc.所以,spring mvc的注解在spring boot总都是可以用的吗? spring b ...

  6. Codeforces 512B: Fox And Jumping

    题目链接 题意说的是,有n种卡片,使用第i种卡片可以使当前自己在数轴上的位置移动 l[i],要获得使用第i种卡片的代价是 c[i],求能使自己移动到数轴上任意位置的最小代价,如果不可能则输出-1 当前 ...

  7. python-Exception异常使用

    Exception #自定义异常类 ,MyInputExcp继承Exception异常 class MyInputExcp(Exception): def __init__(self, lenght, ...

  8. Python3解leetcode Kth Largest Element in a Stream

    问题描述: Design a class to find the kth largest element in a stream. Note that it is the kth largest el ...

  9. KindEditor 完全复制word内容

    Chrome+IE默认支持粘贴剪切板中的图片,但是我要发布的文章存在word里面,图片多达数十张,我总不能一张一张复制吧?Chrome高版本提供了可以将单张图片转换在BASE64字符串的功能.但是无法 ...

  10. Ckeditor IE下粘贴word中图片问题

    自动导入Word图片,或者粘贴Word内容时自动上传所有的图片,并且最终保留Word样式,这应该是Web编辑器里面最基本的一个需求功能了.一般情况下我们将Word内容粘贴到Web编辑器(富文本编辑器) ...