玩爆你的手机联系人--T9搜索
自己研究了好几天联系人的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.显示结果出来.
各位如果觉得容易看得懂,或者自己能用到的,能不能在页面底下投一下您尊贵的一票呢,谢谢!
以上是我的思路,下面附上一段代码
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