通讯录不按拼音排序支持拼音首字母查询么

让 iPhone 联系人中文名单在英文界面下支持排序和首字母定位 - 推酷
让 iPhone 联系人中文名单在英文界面下支持排序和首字母定位
06-25-2013
用 iPhone 的人都知道,联系人列表是可以通过英文名首字母或中文名拼音首字母进行排序,并支持通过右侧的字母表进行快速定位和查找的。但如果你的联系人列表多为中文名,但却又喜欢用英文作为系统语言的话,那么这项功能就失效了。
我的解决方法是在每个联系人名字的后面添加一个拼音首字母,虽然显示起来难看了些,但好歹是又可以排序和定位了。但这个方法不是这次要说的重点,否则正在看这篇文章的你估计砍我的心都有了…如我在「
」这个系列的第二篇中所讲到的,水果喜欢把很多功能藏起来,然后留待用户自己去发现。拿上述提到的这个问题来说,其实水果已经提供了一项用于解决它的功能,只是?被「藏」起来了而已。
这项功能就是为联系人姓氏或名字添加「拼音或音标」。打开联系人详情,点 Edit 进入编辑界面,滚动至最下方,找到 add field ,再点击进入 Add Field 界面,从中在 Phonetic Last Name 或 Phonetic First Name 任选一项,之后回到编辑界面,在 Phonetic Last Name 或 Phonetic First Name 填入你想要的任意字母,保存搞定。
这时,所填入的字母会出现在联系人详情界面的名字下方,而联系人列表不仅在英文界面下也可以按照字母进行排序,同时右侧的字母表也可以正常进行定位操作。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致说起这个算法, 还得提到一个谷歌大神的博客:
快速字符串匹配
前面提到在微软实习时老大扔给我一个Windows Phone让我研究下,我当时玩了玩就觉着不太对劲,找联系人太麻烦。
比如说找”张晓明”,WP只支持定位到Z分类下——这意味着我需要在Z分类下的七十多个联系人(姓张的姓赵的姓钟的等等)里面线性寻找,每次我都需要滑动四五秒才能找到这个张姓少年。
这TMD也太傻逼了,本屌三年前的老破NOKIA都支持首字母定位,996-&ZXM-&张晓明,直接搞定,尼玛一个新时代Windows Phone居然会弱到这个程度。
搜了一下发现没有好用的拨号程序,于是本屌就直接撸了一个支持首字母匹配的拨号程序出来扔到WP论坛里。
结果马上就有各种问题出现——最主要的反映是速度太慢,一些用户甚至反馈按键有时要半秒才有反应。本屌问了下他的通讯录大小:大概3000多人。
吐槽怎么会有这么奇葩的通讯录之余,我意识到自己的字符串匹配算法存在严重的性能问题:读取所有人的姓名计算出拼音,然后一个个的匹配——结果如果联系人数量太多的话,速度必然拙计。
于是我就开始苦思冥想有没有一个能够同时搜索多个字符串的高端算法,以至于那两天坐地铁都在嘟囔怎么才能把这个应用搞的快一些。
最终还是在Algorithms on Strings, Trees and Sequences里找到了答案——确实有能够同时搜索多个字符串的方法:Tries,而且这本书还用足足一章来讲怎么弄Multiple string comparison,看得我当时高潮迭起,直呼过瘾。
具体细节不多说,总之换了算法之后,匹配速度快了大约九十多倍,而且代码还短了几十行。哪怕是有10000个联系人,也能在0.1秒内搞定,速度瓶颈就这样愉快的被算法搞定。
最开始看到的时候心痒, 但大神并没有说出算法. 推荐的书big太高也没看. 想了半天没想到就放弃了. 后来因为工作中用到了, 就又想了半天, 结果想出来了. 我真是个天才(没错, 就是这么凑不要脸).
首字母搜索说明
举个例子就行了. 如:
搜索bc, 下面两个都能搜索出来.
根据列表生成字符tree
再根据字符tree生成索引map
搜索时, 先取两字符去map取出List
遍历list去获取匹配的node中的start和end索引区间
把2中的合起来就行了.
搜索1个字符怎么办(把a-z全压到map里, 或直接遍历就不要用这个搜索了{主要感觉1个字符匹配太多, 不如直接遍历})
搜索字符大于2个(遍历map中get出来的list, 接着匹配子node)
三个文件Main, LetterTree, Node
public class Main {
private static final int A = 65;
private static List&String& testL
private static List&Node&
private static List&String& resultS
private static String searchT
public static void main(String[] args) {
final int listCount = (int) Math.pow(10, 7);
final int charCount = 3;
searchText = getRandomSearchText(2);
log("generateList count: " + listCount
charCount: " + charCount
searchText: " + searchText
if (testList == null) {
log("generateList time: " + testTime(new Runnable() {
public void run() {
testList = generateList(listCount, charCount);
log("sortList time: " + testTime(new Runnable() {
public void run() {
Collections.sort(testList);
final LetterTree lt = new LetterTree();
log("buildTree time: " + testTime(new Runnable() {
public void run() {
lt.buildTree(testList);
long time = testTime(new Runnable() {
public void run() {
result = lt.treeSearch(searchText);
List&Integer& indexList = new ArrayList&&();
int currentIndex = 0;
for (Node item : result) {
for (int i = item.getStartPosition(); i &= currentIndex && i & item.getEndPosition(); i++) {
indexList.add(i);
currentIndex = Math.max(currentIndex, item.getEndPosition());
treeSearch
count: " + indexList.size() + "
time: " + time);
time = testTime(new Runnable() {
public void run() {
resultStrs = LetterTree.forListSearch(searchText, testList);
log("forListSearch
count: " + resultStrs.size() + "
time: " + time);
log("All -----------------------");
log("searchText -----------------------" + searchText);
List&Integer& randomIndexList = new ArrayList&&();
Random rd = new Random();
for (int i = 0; i & 5 && i & resultStrs.size(); i++) {
randomIndexList.add(rd.nextInt(resultStrs.size()));
log("treeSearch -----------------------");
for (int i = 0; i & randomIndexList.size(); i++) {
Integer index = randomIndexList.get(i);
index = indexList.get(index);
log((testList.get(index)));
log("forListSearch -----------------------");
for (int i = 0; i & randomIndexList.size(); i++) {
Integer index = randomIndexList.get(i);
String item = resultStrs.get(index);
log(item);
public static void log(Object obj) {
if (obj == null) obj = "null";
System.out.println(obj.toString());
private static void logTree(Node node, int indent) {
if (node == null) return;
StringBuilder sb = new StringBuilder();
for (int i = 0; i & i++) {
sb.append(' ');
log(sb.toString() + node.getLetter() + "___________________" + node.getStartPosition() + "-" + node.getEndPosition());
for (Node item : node.getList()) {
if (item == null) continue;
logTree(item, indent + 4);
public static long testTime(Runnable runnable) {
long time = System.currentTimeMillis();
runnable.run();
return System.currentTimeMillis() -
public static List&String& generateList(int listCount, int charCount) {
List&String& list = new ArrayList&&(listCount);
Random rd = new Random();
for (int i = 0; i & listC i++) {
StringBuilder sb = new StringBuilder(charCount);
for (int j = 0; j & charC j++) {
sb.append((char) (rd.nextInt(26) + A));
list.add(sb.toString());
public static String getRandomSearchText(int charCount) {
StringBuilder sb = new StringBuilder(charCount);
Random rd = new Random();
for (int j = 0; j & charC j++) {
sb.append((char) (rd.nextInt(26) + A));
return sb.toString();
public class LetterTree {
private HashMap&String, List&Node&& map;
private List&Node&
public static List&String& forListSearch(String text, List&String& list) {
List&String& result = new ArrayList&&();
for (String item : list) {
if (item.contains(text)) result.add(item);
public List&Node& treeSearch(String text) {
if (text == null || text.length() & 2) throw new RuntimeException("字符数必须大于等于两个~!");
List&Node& result = new ArrayList&&();
String anchor = text.substring(0, 2);
List&Node& list = map.get(anchor);
if (list == null || list.size() == 0) return
text = text.substring(2);
if (list == null) list =
if (isEmpty(text)) return list;
for (Node node : list) {
if (node == null)
for (int i = 0; i & text.length(); i++) {
char ch = text.charAt(i);
node = node.getNodeByLetter(ch);
if (node == null)
if (node == null)
result.add(node);
public void buildTree(List&String& list) {
checkNull(list);
root = new Node('A', null);
nodes = new ArrayList&&();
Node currentNode =
for (int index = 0; index & list.size(); index++) {
String str = list.get(index);
if (isEmpty(str))
str = str.toUpperCase();
for (int i = 0; i & str.length(); i++) {
char ch = str.charAt(i);
Node node = currentNode.getNodeByLetter(ch);
if (node == null) {
node = new Node(ch, currentNode);
node.setStartPosition(index);
nodes.add(node);
node.setEndPosition(index + 1);
currentNode =
currentNode =
root.getList();
buildIndex();
public void buildIndex() {
checkNull(root);
map = new HashMap&String, List&Node&&();
StringBuilder sb = new StringBuilder();
for (Node node : nodes) {
Node parent = node.getParent();
if (parent == root)
String anchor = sb.append(parent.getLetter()).append(node.getLetter()).toString();
sb.delete(0, sb.length());
List&Node& list = map.get(anchor);
if (list == null) {
list = new ArrayList&&();
map.put(anchor, list);
list.add(node);
private boolean isEmpty(String str) {
return str == null || str.length() == 0;
private void checkNull(Object obj) {
if (obj == null) throw new NullPointerException("参数为空~!");
public Node getRoot() {
public class Node {
public static final char A = 65;
public static final char Z = 90;
private char
private int startP
private int endP
private Node[]
public Node(char letter, Node currentNode) {
if (letter & A || letter & Z) throw new RuntimeException("char error ---& " + letter);
this.letter =
this.nodes = new Node[26];
setParent(currentNode);
if (currentNode != null) currentNode.setNodeByLetter(letter, this);
public Node getNodeByLetter(char ch) {
int index = getIndexByChar(ch);
return nodes[index];
public void setNodeByLetter(char ch, Node node) {
int index = getIndexByChar(ch);
nodes[index] =
private int getIndexByChar(char ch) {
int index = ch - A;
public char getLetter() {
public void setLetter(char letter) {
this.letter =
public int getStartPosition() {
return startP
public void setStartPosition(int startPosition) {
this.startPosition = startP
public int getEndPosition() {
return endP
public void setEndPosition(int endPosition) {
this.endPosition = endP
if (parent != null) parent.setEndPosition(endPosition);
public Node getParent() {
public void setParent(Node parent) {
this.parent =
public List&Node& getList() {
return Arrays.asList(nodes);
只测了搜索性能, 没加上排序和构建树的时间. 这两可以缓存就没加上去. 看情况, 加上去就杯具了, 最耗时就这两.
for遍历搜索也没想象的那么卡. 看来那个大神卡的原因还是临时计算拼音的问题
读取所有人的姓名计算出拼音,然后一个个的匹配——结果如果联系人数量太多的话,速度必然拙计。
这段代码是来处理算法原理图的那个”这里有坑”的问题的.
有包含重复的问题.
先就这样, 应该还有更好的处理方法.
List&Integer& indexList = new ArrayList&&();
int currentIndex = 0;
for (Node item : result) {
for (int i = item.getStartPosition(); i &= currentIndex && i & item.getEndPosition(); i++) {
indexList.add(i);
currentIndex = Math.max(currentIndex, item.getEndPosition());
github demo
如有更好算法, 欢迎分享出来.
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:874次
排名:千里之外高仿微信通讯录 (联系人拼音首字母排序+搜索功能 )
查看次数:2324
下载次数:637
上传时间:
大小:4 KB
实现微信通讯录UI,联系人按拼音首字母分组排序A~Z,并且含联系人搜索功能。喜欢的小伙伴github上star一个,持续关注哦~
github: /shenAlexy/WeChatContacts
csdn博客地址:http://blog.csdn.net/shenguanhua
个人微信公众号: iOSDevTeam,随时分享iOS开发干货。
您还没有登录!请或
下载过该代码的还下载了
本周热门下载
&2017 Chukong Technologies,Inc.
京公网安备89建议通讯录增加拼音检索功能:就是姓名的首字母,列如(李二)拼音LE。这样方便查找谢谢!愿世界不再有黄牛,愿未来不再需要抢票。
对于接下来的即将要推出的新 iPhone,我们一直都是用“iPhone 8”来称呼它的。毕竟这...
北京时间日早上6点,任天堂《火焰纹章》网络直面会开始。
毕竟新总统上任之后,政策什么的很有可能会发生变化。
苹果认为,企业市场的潜力十分巨大。
虐机又来了,这次找了个特别的地点。
等了这么久终于等到这次更新……
熟悉的口号,但这一次谷歌明显比之前的杀手好多了。
在这款游戏当中,玩家将会化身成为一名勇敢的渔夫,带着先进的武器装备踏上抓捕奇珍异...
书“可以不拘种类,”阅读“也可以不拘形式,但”读书“这件事,还是不要太假于人手,...
平时我们可以通过选择一款游戏,在游戏里里尽情的奔跑、跳跃释放自己心中的压力。《天...
【一款正统RPG 就应该好好讲故事:《天命传说》】本作在遵从了如今手机游戏基本套路的...
一位名为“Haha”的少年来到了一个充满陷阱的迷宫,在这里他需要找到出口并且一路探索...
对于老玩家来说,《仙境传说》是一款极具情怀的作品。
不管你曾经被它震撼过感动过,还是第一次接触到这个闻名遐迩的国产经典之作,拿起手机...
用了这款配件后,无线的AirPods岂不是又变回有线的了?
你要不要也用 iPhone 来帮助自己烧烤呢....
你心目中的AirPods愿望清单又是什么呢~
它就像武士一样,会保护好我们的 iPhone 7 和 iPhone 7 Plus~
NPD 今天表示,AirPods 真正占据的市场份额只有 2%(销量)和 3%(销售额)。
你喜欢轻便的AirPods,还是喜欢其他无线耳机呢~
iPhone版桌面电话,就是那么简单~
对这些广告真的是大爱……
问一个小白问题,怎样在拨号时通过姓名拼音首字母快速查找联系人呢?
注册时间 最后登录
在线时间339 小时 UID
主题帖子人气
小苹果, 积分 41, 距离下一级还需 9 积分
给媳妇,给家人买过n个iphone,帮着越狱,下载软件什么的好几年了,不过自己还真就从来没使用过。
刚整了一台自己用,就遇到这么个小白问题,之前用安卓都是直接拨号时输入姓名拼音首字母来快速定位联系人,但是ios好像就不行。论坛里查找了一下,可能是我搜索的方式不对,没有找到答案。
哪位大侠指点一下,怎么才能实现这个功能,1000多个联系人,靠首字母找真的有点费劲啊。
<p id="rate_36" onmouseover="showTip(this)" tip="QQ通讯录&人气 + 1
" class="mtn mbn">
注册时间 最后登录
在线时间141 小时 UID
主题帖子人气
帮顶下吧。。
注册时间 最后登录
在线时间339 小时 UID
主题帖子人气
求助,顶起来
注册时间 最后登录
在线时间339 小时 UID
主题帖子人气
求助,顶一下
注册时间 最后登录
在线时间166 小时 UID
主题帖子人气
小苹果青苹果红苹果受
banging顶一下吧
小苹果青苹果红苹果受人欺负
注册时间 最后登录
在线时间1056 小时 UID
主题帖子人气
越狱了就简单。
注册时间 最后登录
在线时间191 小时 UID
主题帖子人气
想功能多就越狱吧,大哥
注册时间 最后登录
在线时间341 小时 UID
主题帖子人气
很简单的,你到主屏幕的时候,一直滑动到最左边,在最左边那里,搜索中输入你要找的人啊,信息啊什么的,关键词,关键字就可以了。然后你就能看到联系人。然后你点击进去拨号就好了。
注册时间 最后登录
在线时间176 小时 UID
主题帖子人气
LZ不是说自己会越狱嘛!越狱就可以了。
当然不越狱也可以,很多拨号软件都支持LZ所说的首字母搜索,比如触宝拨号,我觉得还可以。
注册时间 最后登录
在线时间666 小时 UID
主题帖子人气
回 楼主(dutjackson) 的帖子
引用楼主dutjackson于 15:37发表的
:给媳妇,给家人买过n个iphone,帮着越狱,下载软件什么的好几年了,不过自己还真就从来没使用过。刚整了一台自己用,就遇到这么个小白问题,之前用安卓都是直接拨号时输入姓名拼音首字母来快速定位联系人,但......搜索就可以,照样可以输入名字的首字母寻找,很快
威锋旗下产品
Hi~我是威威!
沪公网安备 29号 | 沪ICP备号-1
新三板上市公司威锋科技(836555)
增值电信业务经营许可证:
Powered by Discuz!

我要回帖

更多关于 html5 通讯录a z字母 的文章

 

随机推荐