本文出自 “NashLegend” 博客,请务必保留此出处http://nashlegend.blog.51cto.com/5635342/1566108
完整源码在我的github上 https://github.com/NashLegend/QuicKid
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
public
static
HashMap<Character, String[]> keyMaps;
public
static
void
main(String[] args) {
keyMaps =
new
HashMap<Character, String[]>();
keyMaps.put(
'0'
,
new
String[
0
]);
keyMaps.put(
'1'
,
new
String[
0
]);
keyMaps.put(
'2'
,
new
String[] {
"a"
,
"b"
,
"c"
});
keyMaps.put(
'3'
,
new
String[] {
"d"
,
"e"
,
"f"
});
keyMaps.put(
'4'
,
new
String[] {
"g"
,
"h"
,
"i"
});
keyMaps.put(
'5'
,
new
String[] {
"j"
,
"k"
,
"l"
});
keyMaps.put(
'6'
,
new
String[] {
"m"
,
"n"
,
"o"
});
keyMaps.put(
'7'
,
new
String[] {
"p"
,
"q"
,
"r"
,
"s"
});
keyMaps.put(
'8'
,
new
String[] {
"t"
,
"u"
,
"v"
});
keyMaps.put(
'9'
,
new
String[] {
"w"
,
"x"
,
"y"
,
"z"
});
List<String> lss = getPossibleKeys(
"726"
);
System.out.println(lss.size());
}
public
static
ArrayList<String> getPossibleKeys(String key) {
ArrayList<String> list =
new
ArrayList<String>();
if
(key.length() >
0
) {
if
(key.contains(
"1"
) || key.contains(
"0"
)) {
list.add(key);
}
else
{
int
keyLen = key.length();
String[] words;
if
(keyLen ==
1
) {
words = keyMaps.get(key.charAt(
0
));
for
(
int
i =
0
; i < words.length; i++) {
list.add(words[i]);
}
}
else
{
ArrayList<String> sonList = getPossibleKeys(key.substring(
0
, key.length() -
1
));
words = keyMaps.get(key.charAt(key.length() -
1
));
for
(
int
i =
0
; i < words.length; i++) {
for
(Iterator<String> iterator = sonList.iterator(); iterator
.hasNext();) {
String sonStr = iterator.next();
list.add(sonStr + words[i]);
}
}
}
}
}
return
list;
}
|
下面先定义一下几个匹配规则
-
完全匹配。用来匹配姓名和电话号码。指输入字符串与联系人内某一匹配项完全匹配。无加减分项。
PanZhiHui-->PanZhiHui
-
前置首字母完全匹配。用来匹配姓名。指输入字符串与联系人前几个首字母完全匹配。用来匹配姓名。是前置首字母溢出匹配的特殊形式。 无加分项,减分项为不匹配的首字母个数。
PZH-->PanZhiHui
。+2-0
PZ-->PanZhiHui
。+2-1 -
前置首字母溢出匹配。用来匹配姓名。指在匹配首字母的情况下,还匹配了某一个或者几个首字母后一段连贯的字符串。加分项为匹配到的首字母个数,减分项为不匹配的首字母个数。
PanZH-->PanZhiHui
。+1-0
PZhiHui-->PanZhiHui
。+1-0
PZHui-->PanZhiHui
。+1-0
PZHu-->PanZhiHui
。+1-0
PZhi-->PanZhiHui
。+1-1 -
前置段匹配。用来匹配姓名。指一个长度为N的连贯字符与联系人内某一匹配项的前N个字符完全匹配。是前置首字母溢出匹配的特殊形式。
panzh-->PanZhiHui
-
后置首字母完全匹配。用来匹配姓名。指输入字符串匹配除第一个首字母以外的其他几个连续首字母。 无加分项,减分项为不匹配的首字母个数。
ZH-->PanZhiHui
-
后置首字母溢出匹配。用来匹配姓名。后置首字母完全匹配的情况下,还匹配了某一个或者几个首字母后一段连贯的字符串。加分项为匹配的首字母的数量,减分项为不匹配的首字母个数。
ZHu-->PanZhiHui
。+1-0
Zh-->PanZhiRui
。+1-1 -
后置段匹配。用来匹配姓名。指有一串长度为N的连贯字符与与联系人内某一匹配项的后半部的一段N个字符串匹配,且此连贯字符的开头位置必须是某一首字母位置。是后置首字母溢出匹配的特殊形式,同时意味着后置首字母溢出匹配事实上不需要加分项,只要保证后置首字母完全匹配的加分项比它大就足够了。
ZhiHui/Zhi/Hui-->PanZhiHui
-
后置无头匹配。用来匹配姓名和电话号码。指一串连贯字符在前7种全部未匹配成功的情况下,却被包含在字符串里。加分项为-index,减分项为长度差
hiHui-->PanZhiHui
排列规则
-
查询出的列表将按匹配度排序,匹配度是一个float(当然double也一样),优先级别从高到低如下(减分项足够小以至于高优先级的匹配度无论如何减分都仍然会高于下面的优先级,因此减分项事实上只用来区别同一优先级中不同联系人匹配程度的高低)。
-
完全匹配,对应的基础数值为4000。
-
前置首字母完全匹配、前置首字母溢出匹配、前置段匹配,这三个其实都可以视作前置首字母溢出匹配,对应的基础数值为3000。(当只有一个字母时,按规则#1算)
-
后置首字母完全匹配、后置首字母溢出匹配、后置段匹配,这三个其实都可以视作后置首字母溢出匹配。对应的基础数值为2000。(当只有一个字母时,按规则#5算)
-
后置无头匹配。对应的基础数值为1000。(可以考虑摒弃此匹配,没有人会这么按,而按键出错的可能性导致无头匹配的可能性又极小,往往不是想要的结果)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
static
final
int
Match_Type_Name =
1
;
static
final
int
Match_Type_Phone =
2
;
static
final
int
Level_Complete =
4
;
static
final
int
Level_Fore_Acronym_Overflow =
3
;
static
final
int
Level_Back_Acronym_Overflow =
2
;
static
final
int
Level_Headless =
1
;
static
final
int
Level_None =
0
;
static
final
float
Match_Level_None =
0
;
static
final
float
Match_Level_Headless =
1000
;
static
final
float
Match_Level_Back_Acronym_Overflow =
2000
;
static
final
float
Match_Level_Fore_Acronym_Overflow =
3000
;
static
final
float
Match_Level_Complete =
4000
;
static
final
float
Match_Score_Reward =
1
;
static
final
float
Match_Miss_Punish =
0
.001f;
static
final
int
Max_Reward_Times =
999
;
static
final
int
Max_Punish_Times =
999
;
|
1
2
3
|
List<ArrayList<String>> fullNameNumber =
new
ArrayList<ArrayList<String>>();
List<String> fullNameNumberWithoutSpace =
new
ArrayList<String>();
List<String> abbreviationNumber =
new
ArrayList<String>();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
synchronized
public
void
initPinyin() {
String trimmed = name.replaceAll(
" "
,
""
);
//将姓名转化为拼音
String fullNamesString = HanyuPinyinHelper.hanyuPinYinConvert(trimmed,
false
);
for
(Iterator<String> iterator = fullNamesString.iterator(); iterator
.hasNext();) {
String str = iterator.next();
ArrayList<String> lss =
new
ArrayList<String>();
String[] pinyins = TextUtil.splitIgnoringEmpty(str,
" "
);
String abbra =
""
;
String fullNameNumberWithoutSpaceString =
""
;
for
(
int
i =
0
; i < pinyins.length; i++) {
String string = pinyins[i];
String res = convertString2Number(string);
abbra += res.charAt(
0
);
fullNameNumberWithoutSpaceString += res;
lss.add(res);
}
abbreviationNumber.add(abbra);
fullNameNumberWithoutSpace
.add(fullNameNumberWithoutSpaceString);
fullNameNumber.add(lss);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public
float
match(String reg) {
// 无法通过第一个字母来判断是不是后置匹配
// 但是可以通过第一个字母判断是不是前置匹配
// match的原则是匹配尽可能多的字符
// 事实上前五种匹配方式都可以使用crossMatch来实现
ScoreAndHits scoreAndHits =
new
ScoreAndHits(-
1
, 0f,
new
ArrayList<PointPair>());
if
(!TextUtils.isEmpty(reg)) {
boolean
checkBack = !canPrematch(reg);
if
(!checkBack) {
if
((scoreAndHits = completeMatch(reg)).score == 0f) {
if
((scoreAndHits = foreAcronymOverFlowMatch(reg)).score == 0f) {
checkBack =
true
;
}
}
}
if
(checkBack) {
if
((scoreAndHits = backAcronymOverFlowMatch(reg)).score == 0f) {
scoreAndHits = backHeadlessParagraphMatch(reg);
}
}
}
scoreAndHits.reg = reg;
matchValue = scoreAndHits;
return
scoreAndHits.score;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
static
class
ScoreAndHits {
public
float
score = 0f;
public
int
nameIndex;
public
ArrayList<PointPair> pairs =
new
ArrayList<PointPair>();
public
int
matchType = Match_Type_Name;
public
int
matchLevel = Level_None;
public
String reg =
""
;
public
ScoreAndHits(
int
nameIndex,
float
score,
ArrayList<PointPair> pairs) {
this
.nameIndex = nameIndex;
this
.score = score;
this
.pairs = pairs;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
private
ScoreAndHits completeMatch(String reg) {
ScoreAndHits scoreAndHits =
new
ScoreAndHits(-
1
, 0f,
new
ArrayList<PointPair>());
for
(
int
i =
0
; i < fullNameNumberWithoutSpace.size(); i++) {
String str = fullNameNumberWithoutSpace.get(i);
if
(reg.equals(str)) {
scoreAndHits.nameIndex = i;
scoreAndHits.score = Match_Level_Complete;
scoreAndHits.pairs.add(
new
PointPair(i, -
1
));
scoreAndHits.matchLevel = Level_Complete;
return
scoreAndHits;
}
}
for
(
int
i =
0
; i < phones.size(); i++) {
PhoneStruct phone = phones.get(i);
if
(reg.equals(phone.phoneNumber)) {
scoreAndHits.nameIndex = i;
scoreAndHits.score = Match_Level_Complete;
scoreAndHits.pairs.add(
new
PointPair(i, -
1
));
scoreAndHits.matchType = Match_Type_Phone;
scoreAndHits.matchLevel = Level_Complete;
return
scoreAndHits;
}
}
// 走到这里说明没有匹配
return
new
ScoreAndHits(-
1
, 0f,
new
ArrayList<PointPair>());
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
private
ScoreAndHits foreAcronymOverFlowMatch(String reg) {
// 因为有可能是多音字,所以这个方法用来对比不同拼音的匹配度,并取最大的那个
ScoreAndHits scoreAndHits =
new
ScoreAndHits(-
1
, 0f,
new
ArrayList<PointPair>());
for
(
int
i =
0
; i < fullNameNumber.size(); i++) {
ArrayList<String> names = fullNameNumber.get(i);
ScoreAndHits tmpscore = foreAcronymOverFlowMatch(names, reg);
if
(tmpscore.score > scoreAndHits.score) {
scoreAndHits = tmpscore;
scoreAndHits.nameIndex = i;
}
}
scoreAndHits.matchLevel = Level_Fore_Acronym_Overflow;
return
scoreAndHits;
}
// 在第一个字母确定的情况下,第二个字母有可能有三种情况
// 一、在第一个字母所在单词的邻居位置charAt(x+1);
// 二、在第二个单词的首字母处
// 三、以上两种情况皆不符合,不匹配,出局
private
ScoreAndHits foreAcronymOverFlowMatch(ArrayList<String> names,
String reg) {
// 用来得出某一个拼音的匹配值。
ScoreAndHits scoreAndHits =
new
ScoreAndHits(-
1
, 0f,
new
ArrayList<PointPair>());
if
(names.get(
0
).charAt(
0
) == reg.charAt(
0
)) {
//其实crossWords()方法才是求匹配值的方法,lol
OverflowMatchValue value = crossWords(names, reg,
0
,
0
,
0
);
int
cross = crossWords(names, reg,
0
,
0
,
0
).crossed;
if
(cross >
0
) {
scoreAndHits.score = Match_Level_Fore_Acronym_Overflow + cross
* Match_Score_Reward - (names.size() - cross)
* Match_Miss_Punish;
scoreAndHits.pairs = value.pairs;
}
}
return
scoreAndHits;
}
/**
* 返回一串字符能跨越另一串字符的长度,根据上面的匹配规则,要尽可能的多匹配单词。若要保证
* 能匹配最长的长度,只要保证下一个字符开始的一段字符能匹配最长的长度即可,换名话说,
* 如果想要让96758匹配最长的字符串,那么只要保证6758能匹配最长的字符串即可,
* 然后758,再然后58……。例如,名字叫PanAnNing,输入pan,那么应该匹配三个首字母,
* PAN,而不是第一姓的拼音Pan.这是一个递归。
*
*
* @param names
* @param regString
* 匹配字符串
* @param listIndex
* 匹配到的list的第listIndex个单词
* @param strIndex
* 匹配到第listIndex个单词中的第strIndex个字母
* @param regIndex
* regchar的匹配位置,比如匹配到了96758的7上,也就是regIndex==2.
* @return
*/
private
OverflowMatchValue crossWords(ArrayList<String> names,
String regString,
int
listIndex,
int
strIndex,
int
regIndex) {
//在进入此方法时,第listIndex个单词的第strIndex的字母肯定是
// 与regString的第regIndex个字母相等的
OverflowMatchValue result =
new
OverflowMatchValue(
0
,
false
);
OverflowMatchValue reser =
new
OverflowMatchValue(
0
,
false
);
//返回如果匹配到本单词的下一个字母能得到的匹配值
OverflowMatchValue impul =
new
OverflowMatchValue(
0
,
false
);
//返回如果匹配到下一个单词的第一个字母的匹配值
// 仍然以【名字叫PanAnNing,输入pan(其实对比的是数字,这里转化成字母为了方便)】举例
// 假设这时listIndex,strIndex,regIndex都是0,所以现在匹配的是p字母,它毫无疑问对应姓名的第一个P,
// 那么下一步应该怎么做呢,由上面所说【保证下一个字符开始的一段字符能匹配最长的长度即可】
// 也就是说,我们输入的pan中的第二个字母a匹配哪个位置将得到最优结果。这个盒子中显然有两种情况。
// 一是匹配姓氏Pan中的a,另一个是匹配名字AnNing中的A。
// reser就表示如果a匹配到Pan中的a最终的匹配值。
// impul就表示如果a匹配到AnNing中的A得到的最终的匹配值。
if
(regIndex < regString.length() -
1
) {
//如果还没匹配到最后一个字母,也就是regString还没匹配到最后一个,那么将检测如
//果将regString的下一个字母放到哪里将得到最优结果
char
nextChar = regString.charAt(regIndex +
1
);
if
(listIndex < names.size() -
1
&& nextChar == names.get(listIndex +
1
).charAt(
0
)) {
impul = crossWords(names, regString, listIndex +
1
,
0
,
regIndex +
1
);
}
if
(strIndex < names.get(listIndex).length() -
1
&& nextChar == names.get(listIndex).charAt(strIndex +
1
)) {
reser = crossWords(names, regString, listIndex, strIndex +
1
,
regIndex +
1
);
}
//如果上面两个条件都不成立,那么就表示本次匹配失败
}
else
{
result =
new
OverflowMatchValue((strIndex ==
0
) ?
1
:
0
,
true
);
result.pairs.add(
0
,
new
PointPair(listIndex, strIndex));
}
if
(reser.matched || impul.matched) {
//如果其中任意一个方式可以匹配,那么结果最大的那个就是最优结果
if
(impul.crossed > reser.crossed) {
result = impul;
}
else
{
result = reser;
}
result.matched =
true
;
result.crossed = ((strIndex ==
0
) ?
1
:
0
)
+ Math.max(result.crossed, result.crossed);
result.pairs.add(
0
,
new
PointPair(listIndex, strIndex));
}
return
result;
}
static
class
OverflowMatchValue {
public
int
crossed =
0
;
public
boolean
matched =
false
;
public
ArrayList<PointPair> pairs =
new
ArrayList<PointPair>();
public
OverflowMatchValue(
int
c,
boolean
m) {
this
.crossed = c;
this
.matched = m;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
private
ScoreAndHits backAcronymOverFlowMatch(String reg) {
//跟上面差不多
ScoreAndHits scoreAndHits =
new
ScoreAndHits(-
1
, 0f,
new
ArrayList<PointPair>());
for
(
int
i =
0
; i < fullNameNumber.size(); i++) {
ArrayList<String> names = fullNameNumber.get(i);
ScoreAndHits tmp = backAcronymOverFlowMatch(names, reg);
if
(tmp.score > scoreAndHits.score) {
scoreAndHits = tmp;
scoreAndHits.nameIndex = i;
}
}
scoreAndHits.matchLevel = Level_Back_Acronym_Overflow;
return
scoreAndHits;
}
private
ScoreAndHits backAcronymOverFlowMatch(ArrayList<String> names,
String reg) {
int
score =
0
;
int
punish =
0
;
ScoreAndHits scoreAndHits =
new
ScoreAndHits(-
1
, 0f,
new
ArrayList<PointPair>());
// 有可能会调用多次crossWords,取决于名字的长度。这是跟前面的不同
for
(
int
i =
0
; i < names.size(); i++) {
String string = (String) names.get(i);
if
(string.charAt(
0
) == reg.charAt(
0
)) {
OverflowMatchValue value = crossWords(names, reg, i,
0
,
0
);
int
cross = value.crossed;
int
lost = names.size() - cross;
if
(cross > score || cross == score && punish > lost) {
scoreAndHits.pairs = value.pairs;
score = cross;
punish = lost;
}
}
}
if
(score >
0
) {
scoreAndHits.score = Match_Level_Back_Acronym_Overflow + score
* Match_Score_Reward - punish * Match_Miss_Punish;
return
scoreAndHits;
}
else
{
return
new
ScoreAndHits(-
1
, 0f,
new
ArrayList<PointPair>());
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
private
ScoreAndHits backHeadlessParagraphMatch(String reg) {
// TODO,如果此人有两个相似的号码,那么就只能匹配出一个来了,这是很显然不对的
int
punish =
0
;
ScoreAndHits scoreAndHits =
new
ScoreAndHits(-
1
, -1f,
new
ArrayList<PointPair>());
scoreAndHits.matchLevel = Level_Headless;
scoreAndHits.matchType = Match_Type_Phone;
// 不匹配姓名
for
(
int
i =
0
; i < phones.size(); i++) {
PhoneStruct phone = phones.get(i);
int
sco = phone.phoneNumber.indexOf(reg);
if
(sco >=
0
) {
int
lost = phone.phoneNumber.length() - reg.length();
if
(scoreAndHits.score < sco || sco == scoreAndHits.score
&& punish > lost) {
scoreAndHits.score = sco;
scoreAndHits.nameIndex = i;
punish = lost;
}
//pairs.add放到判断外面是因为有可能匹配到同一个人的多个手机号码。
scoreAndHits.pairs.add(
new
PointPair(i, sco));
}
}
if
(scoreAndHits.score >=
0
) {
scoreAndHits.score = Match_Level_Headless - scoreAndHits.score
* Match_Score_Reward - punish * Match_Miss_Punish;
}
return
scoreAndHits;
}
//表示电话号码的一个静态类,将过滤掉开头的+86以及系统可能自动生成的“-”以及其他非数字的字符以便于搜索
public
static
class
PhoneStruct {
public
String phoneNumber;
public
int
phoneType;
public
String displayType;
public
PhoneStruct(String number,
int
type) {
phoneNumber = number.replaceAll(
"^\\+86"
,
""
).replaceAll(
"[\\]+"
,
""
);
phoneType = type;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
@Override
public
Filter getFilter() {
return
filter;
}
// 上一次搜索的字符串
private
String preQueryString =
""
;
private
Filter filter =
new
Filter() {
@Override
protected
void
publishResults(CharSequence constraint,
FilterResults results) {
if
(results !=
null
) {
if
(results.count >
0
) {
notifyDataSetChanged();
}
else
{
notifyDataSetInvalidated();
}
}
}
@Override
synchronized
protected
FilterResults performFiltering(CharSequence constraint) {
if
(TextUtils.isEmpty(constraint)
|| preQueryString.equals(constraint)) {
return
null
;
}
String queryString = constraint.toString();
FilterResults results =
new
FilterResults();
int
preLength = preQueryString.length();
int
queryLength = queryString.length();
ArrayList<Contact> baseList =
new
ArrayList<Contact>();
ArrayList<Contact> resultList =
new
ArrayList<Contact>();
if
(preLength >
0
&& (preLength == queryLength -
1
)
&& queryString.startsWith(preQueryString)) {
//如果本次搜索的字符串是上次搜索的字符串开头,那么将只在contacts里面搜索(contacts是当前列表的数据集合)
baseList = contacts;
}
else
{
//过滤所有联系人
baseList = AllContacts;
}
for
(Iterator<Contact> iterator = baseList.iterator(); iterator
.hasNext();) {
Contact contact = (Contact) iterator.next();
if
(contact.match(queryString) >
0
) {
resultList.add(contact);
}
}
sortContact(resultList);
// 这是ContactAdapter中的方法,将ContactAdapter的数据换成resultList。
preQueryString = queryString;
results.values = resultList;
results.count = resultList.size();
setContacts(resultList);
return
results;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
if
(contact.matchValue.matchLevel == Contact.Level_Complete) {
//如果是完全匹配,那么只要全部高亮对应的姓名拼音或者电话号码就OK了
if
(contact.matchValue.matchType == Contact.Match_Type_Name) {
String str = contact.fullNamesString.get(
contact.matchValue.nameIndex).replaceAll(
" "
,
""
);
SpannableStringBuilder builder =
new
SpannableStringBuilder(
str);
ForegroundColorSpan redSpan =
new
ForegroundColorSpan(
Color.RED);
builder.setSpan(redSpan,
0
, str.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
pinyinTextView.setText(builder);
}
else
{
shouldDisplayMorePhones =
false
;
String str = contact.getPhones().get(
contact.matchValue.nameIndex).phoneNumber;
SpannableStringBuilder builder =
new
SpannableStringBuilder(
str);
ForegroundColorSpan redSpan =
new
ForegroundColorSpan(
Color.RED);
builder.setSpan(redSpan,
0
, str.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
phoneTextView.setText(builder);
}
}
else
if
(contact.matchValue.matchLevel == Contact.Level_Headless) {
//如果是后置无头匹配,那么高亮从strIndex开始的regString长度的一串就行了
shouldDisplayMorePhones =
false
;
String str = contact.getPhones().get(
contact.matchValue.nameIndex).phoneNumber;
SpannableStringBuilder builder =
new
SpannableStringBuilder(str);
ForegroundColorSpan redSpan =
new
ForegroundColorSpan(Color.RED);
builder.setSpan(redSpan,
contact.matchValue.pairs.get(
0
).strIndex,
contact.matchValue.pairs.get(
0
).strIndex
+ contact.matchValue.reg.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
phoneTextView.setText(builder);
for
(
int
i =
1
; i < contact.matchValue.pairs.size(); i++) {
int
idx = contact.matchValue.pairs.get(i).listIndex;
PhoneStruct phoneStruct = contact.getPhones().get(idx);
PhoneView phoneView =
new
PhoneView(getContext());
phoneView.setPhone(phoneStruct, contact.matchValue.reg);
phoneViews.addView(phoneView);
}
}
else
{
// 剩下的情况就是两个首字母匹配了。首字母匹配到的字符串位置不是连续的
// 匹配到的字母一个一个记录在contact.matchValue.pairs里面
// 所以要先将contact.matchValue.pairs里的一个个不连续的字母连接成几个字符串
String str = contact.fullNamesString.get(
contact.matchValue.nameIndex).replaceAll(
" "
,
""
);
ArrayList<PointPair> pa = getColoredString(
contact.fullNameNumber
.get(contact.matchValue.nameIndex),
contact.matchValue.pairs,
"#FF0000"
);
SpannableStringBuilder builder =
new
SpannableStringBuilder(str);
for
(Iterator<PointPair> iterator = pa.iterator(); iterator
.hasNext();) {
PointPair pointPair = iterator.next();
builder.setSpan(
new
ForegroundColorSpan(Color.RED),
pointPair.listIndex, pointPair.strIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
pinyinTextView.setText(builder);
}
// getColoredString是将PointPairs列表单个的字符转化成几个字符串范围。这时候返回的PointPair的listIndex
// 变成了字符串开关的位置,strIndex变成了长度。builder.setSpan将使这几段范围内的字符高亮
private
ArrayList<PointPair> getColoredString(ArrayList<String> strings,
ArrayList<PointPair> pairs, String color) {
int
k =
0
;
int
idx = -
1
;
int
crtHead = -
1
;
int
crtTail = -
1
;
ArrayList<PointPair> ps =
new
ArrayList<PointPair>();
for
(
int
i =
0
; i < strings.size(); i++) {
String str = strings.get(i);
for
(
int
j =
0
; j < str.length() && k < pairs.size(); j++) {
idx++;
if
(pairs.get(k).listIndex == i && pairs.get(k).strIndex == j) {
if
(crtHead == -
1
) {
crtHead = idx;
crtTail = idx +
1
;
}
else
{
if
(crtTail == idx) {
crtTail = idx +
1
;
}
}
k++;
}
else
{
if
(crtHead != -
1
) {
ps.add(
new
PointPair(crtHead, crtTail));
crtHead = -
1
;
crtTail = -
1
;
}
}
}
}
if
(crtHead != -
1
) {
ps.add(
new
PointPair(crtHead, crtTail));
crtHead = -
1
;
crtTail = -
1
;
}
return
ps;
}
|