自己研究了好几天联系人的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. weblogic+idea

    1.首先是weblogic的安装,去官网下载,下载完成后,安装,然后还需要创建域,参考链接: https://www.cnblogs.com/xdp-gacl/p/4140683.html,创建域的h ...

  2. CentOS7 设置电源选项,待机、睡眠、挂起

    设置装有 CentOS7 的笔记本合盖后黑屏进入睡眠模式 systemd 能够处理某些电源相关的 ACPI事件,你可以通过从 /etc/systemd/logind.conf 以下选项进行配置: Ha ...

  3. 【LeetCode】数学(共106题)

    [2]Add Two Numbers (2018年12月23日,review) 链表的高精度加法. 题解:链表专题:https://www.cnblogs.com/zhangwanying/p/979 ...

  4. SOAP、WSDL、 UDDI之间的关系

    SOAP(Simple Object Access Protocol) 简单对象访问协议: WSDL(Web Services Description Language) Web服务描述语言: UDD ...

  5. 2019届校招前端面试题整理——HTML、CSS篇

    前言 2019届校招陆陆续续开始了,整理了一些高频的面试题. HTML部分 1. 什么是<!DOCTYPE>? DOCTYPE是html5标准网页声明,且必须声明在HTML文档的第一行.来 ...

  6. Mardown加上目录

    适合Jekyll+Github模式下post.html 中加入如下代码,会在页面加载时生成目录结构: 有两种方案: 方案一效果

  7. 修改linux的mysql用户名和密码

    MySQL数据库密码忘记之后,可以进入linux下修改原始密码,步骤为下.第一步:登陆服务器管理员权限.第二步:进入MySQL数据配置文件 [root@VM_0_8_centos ~]# vi /et ...

  8. Xib中用自动布局设置UIScrollView的ContenSize

    1. 在UIScrollView上拖一个UIView 2.设置UIScrollView上下左右约束为0,设置UIView上下左右约束为0,并且设置水平中线约束.那么可以把水平中线约束拖到对应视图,利用 ...

  9. 【Java】Java调用第三方接口

    Get请求与Http请求 https://www.w3school.com.cn/tags/html_ref_httpmethods.asp HttpClient HTTP 协议可能是现在 Inter ...

  10. 【2019 Multi-University Training Contest 9】

    01: 02:https://www.cnblogs.com/myx12345/p/11666961.html 03: 04: 05:https://www.cnblogs.com/myx12345/ ...