一个电话本中联系人名字搜索高亮的实现
需求:实现电话本名字搜索功能的高亮实现,其中搜索支持中文、简拼、全拼搜索,对搜索匹配到的名字部分高亮显示。
例如:
名字:张向东
全拼:zhangxiangdong
简拼:zxd
搜索匹配优先规则:中文(直接匹配)、简拼匹配、全拼匹配
关键在全拼匹配上面,比方用户输入gd,则匹配向东两个字,同xiangdong的匹配项
搜索功能通过数据库的搜索匹配实现,也就是输入是搜索的输入字符串mInputStr和搜索的名字结果列表List<User>
SearchHighLightUtil的构造函数
上代码:
第一版:PinyinUtil为使用开源的汉字转拼音的Jar包
代码语言:javascript复制import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SearchHighLightUtil {
/*
* 输入的搜索字符串
*/
private String mInputStr;
/*
* 输入的搜索结果
*/
private List<User> mListResult;
/*
* 用名字作为关键字,得到匹配名字的起始位置和结束位置
*/
private Map<String, SearchKeyPosition> mAnalyzeResult;
/*
* 构造方法
*/
public SearchHighLightUtil(String inputStr, List<User> listResult){
this.mInputStr = inputStr;
this.mListResult = listResult;
/*
分析输入的字符串和搜索匹配的结果,将分析的结果存储在mAnalyzeResult中,用名字作为关键字,得到匹配名字的起始位置和结束位置
*/
this.analyze();
}
public Map<String, SearchKeyPosition> getAnalyzeResult(){
return mAnalyzeResult;
}
private void analyze(){
if (mAnalyzeResult == null){
mAnalyzeResult = new HashMap<String, SearchKeyPosition>();
}
if (mListResult == null || mInputStr == null){
return;
}
for(int i=0; i<mListResult.size(); i ){
String name = mListResult.get(i).getName();
String jpName = mListResult.get(i).getJPName();
String qpName = mListResult.get(i).getQPName();
String []jpNameArr = jpName.split("#");
if (jpNameArr.length > 1){
if (jpNameArr[0].contains(mInputStr)){
jpName = jpNameArr[0];
}else if (jpNameArr[1].contains(mInputStr)){
jpName = jpNameArr[1];
}
}
if (name.contains(mInputStr)){
int start = name.indexOf(mInputStr);
int end = start mInputStr.length() - 1;
mAnalyzeResult.put(name, new SearchKeyPosition(start, end));
}else if (jpName.contains(mInputStr)){
int start = jpName.indexOf(mInputStr);
int end = start mInputStr.length() - 1;
mAnalyzeResult.put(name, new SearchKeyPosition(start, end));
}else if (qpName.contains(mInputStr)){
int start = qpName.indexOf(mInputStr);
if (start == -1){
continue;
}
int end = mInputStr.length();
int nameLength = jpName.length() > name.length()?name.length():jpName.length();
switch(nameLength){
case 2:
{
String firstPinYin = PinyinUtil.getFullSpell(name.substring(0, 1));
int col = (start mInputStr.length());
if (start <= (firstPinYin.length() - 1)){
if (col <= firstPinYin.length()){
start = 0;
end = 0;
}else{
start = 0;
end = 1;
}
}else {
start = 1;
end = 1;
}
break;
}
case 3:
{
String firstPinYin = PinyinUtil.getFullSpell(name.substring(0, 1));
String sencondPinYin = PinyinUtil.getFullSpell(name.substring(1,2));
int col = (start mInputStr.length());
if (start <= (firstPinYin.length() - 1)){
if (col <= firstPinYin.length()){
start = 0;
end = 0;
}else if (col <= (firstPinYin.length() sencondPinYin.length())){
start = 0;
end = 1;
}else {
start = 0;
end = 2;
}
}else if (start <= (firstPinYin.length() sencondPinYin.length()- 1)){
if (col <= (firstPinYin.length() sencondPinYin.length())){
start = 1;
end = 1;
} else {
start = 1;
end = 2;
}
}else{
start = 2;
end = 2;
}
}
break;
case 4:
{
String firstPinYin = PinyinUtil.getFullSpell(name.substring(0, 1));
String sencondPinYin = PinyinUtil.getFullSpell(name.substring(1,2));
String thirdPinYin = PinyinUtil.getFullSpell(name.substring(2,3));
int col = (start mInputStr.length());
if (start <= (firstPinYin.length() - 1)){
if (col <= firstPinYin.length()){
start = 0;
end = 0;
}else if (col <= (firstPinYin.length() sencondPinYin.length())){
start = 0;
end = 1;
}else if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length())){
start = 0;
end = 2;
}else{
start = 0;
end = 3;
}
}else if (start <= (firstPinYin.length() sencondPinYin.length()- 1)){
if (col <= (firstPinYin.length() sencondPinYin.length())){
start = 1;
end = 1;
} else if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length())){
start = 1;
end = 2;
}else{
start = 1;
end = 3;
}
}else if (start <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length()- 1)){
if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length())){
start = 2;
end = 2;
} else {
start = 2;
end = 3;
}
}else{
start = 3;
end = 3;
}
}
break;
case 5:
{
String firstPinYin = PinyinUtil.getFullSpell(name.substring(0, 1));
String sencondPinYin = PinyinUtil.getFullSpell(name.substring(1,2));
String thirdPinYin = PinyinUtil.getFullSpell(name.substring(2,3));
String fourthPinYin = PinyinUtil.getFullSpell(name.substring(3,4));
int col = (start mInputStr.length());
if (start < (firstPinYin.length() - 1)){
if (col <= firstPinYin.length()){
start = 0;
end = 0;
}else if (col <= (firstPinYin.length() sencondPinYin.length())){
start = 0;
end = 1;
}else if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length())){
start = 0;
end = 2;
}else if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length() fourthPinYin.length())){
start = 0;
end = 3;
}else{
start = 0;
end = 4;
}
}else if (start < (firstPinYin.length() sencondPinYin.length()- 1)){
if (col <= (firstPinYin.length() sencondPinYin.length())){
start = 1;
end = 1;
} else if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length())){
start = 1;
end = 2;
}else if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length() fourthPinYin.length())){
start = 1;
end = 3;
}else{
start = 1;
end = 4;
}
}else if (start < (firstPinYin.length() sencondPinYin.length() thirdPinYin.length()- 1)){
if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length())){
start = 2;
end = 2;
}else if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length() fourthPinYin.length())){
start = 2;
end = 3;
} else {
start = 2;
end = 4;
}
}else if (start < (firstPinYin.length() sencondPinYin.length() thirdPinYin.length() fourthPinYin.length()- 1)){
if (col <= (firstPinYin.length() sencondPinYin.length() thirdPinYin.length() fourthPinYin.length())){
start = 3;
end = 3;
} else {
start = 3;
end = 4;
}
}else {
start = 4;
end = 4;
}
}
break;
default:
start = -1;
end = -1;
break;
}
mAnalyzeResult.put(name, new SearchKeyPosition(start, end));
}
}
}
public static class User{
private String name;
private String jpName;
private String qpName;
public String getName(){
return name;
}
public String getJPName(){
return jpName;
}
public String getQPName(){
return qpName;
}
}
public static class SearchKeyPosition{
public int start = -1;
public int end = -1;
public SearchKeyPosition(int start, int end){
this.start = start;
this.end = end;
}
}
}
得到搜索的字符串在名字中匹配的开始位置和结束位置后,就可以设置高亮了:
/**
* 关键字高亮变色
*
* @param color
* 变化的色值
* @param text
* 文字
* @param keyword
* 文字中的关键字
* @return
*/
public static SpannableString matcherSearchTitle(int color, String text,
String keyword) {
SpannableString s = new SpannableString(text);
Pattern p = Pattern.compile(keyword);
Matcher m = p.matcher(s);
while (m.find()) {
int start = m.start();
int end = m.end();
s.setSpan(new ForegroundColorSpan(color), start, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return s;
}
第二版:
主要对于全拼匹配的部分做了一个优化,第一版本对有几个汉字有约束,并且最大支持5个汉字,扩展性不好,第二版本改进了这个问题,用循环的方式实现;这样的改进也算是对思路的一个梳理。
代码语言:javascript复制import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SearchHighLightUtil {
/*
* 输入的搜索字符串
*/
private String mInputStr;
/*
* 输入的搜索结果
*/
private List<User> mListResult;
/*
* 用名字作为关键字,得到匹配名字的起始位置和结束位置
*/
private Map<String, SearchKeyPosition> mAnalyzeResult;
/*
* 构造方法
*/
public SearchHighLightUtil(String inputStr, List<User> listResult){
this.mInputStr = inputStr;
this.mListResult = listResult;
/*
分析输入的字符串和搜索匹配的结果,将分析的结果存储在mAnalyzeResult中,用名字作为关键字,得到匹配名字的起始位置和结束位置
*/
this.analyze();
}
public Map<String, SearchKeyPosition> getAnalyzeResult(){
return mAnalyzeResult;
}
private void analyze(){
if (mAnalyzeResult == null){
mAnalyzeResult = new HashMap<String, SearchKeyPosition>();
}
if (mListResult == null || mInputStr == null){
return;
}
for(int i=0; i<mListResult.size(); i ){
String name = mListResult.get(i).getName();
String jpName = mListResult.get(i).getJPName();
String qpName = mListResult.get(i).getQPName();
String []jpNameArr = jpName.split("#");
if (jpNameArr.length > 1){
if (jpNameArr[0].contains(mInputStr)){
jpName = jpNameArr[0];
}else if (jpNameArr[1].contains(mInputStr)){
jpName = jpNameArr[1];
}
}
if (name.contains(mInputStr)){
int start = name.indexOf(mInputStr);
int end = start mInputStr.length() - 1;
mAnalyzeResult.put(name, new SearchKeyPosition(start, end));
}else if (jpName.contains(mInputStr)){
int start = jpName.indexOf(mInputStr);
int end = start mInputStr.length() - 1;
mAnalyzeResult.put(name, new SearchKeyPosition(start, end));
}else if (qpName.contains(mInputStr)){
int start = qpName.indexOf(inputStr);
if (start == -1){
continue;
}
int end = -1;
int lastLength = 0;
int col = (start inputStr.length());
for (int j = 0; j<name.length(); j ){
String pinYin = PinyinUtil.getFullSpell(name.substring(j, j 1));
lastLength = pinYin.length();
if (start <= lastLength - 1){
start = j;
int lastEndLength = lastLength;
if (col <= lastEndLength){
end = j;
mAnalyzeResult.put(name, new TapPosition(start, end));
}else {
for (int k = j 1; k < name.length(); k ) {
pinYin = PinyinUtil.getFullSpell(name.substring(k, k 1));
lastEndLength = pinYin.length();
if (col <= lastEndLength) {
end = k;
mAnalyzeResult.put(name, new TapPosition(start, end));
break;
}
}
}
break;
}
}
}
}
}
public static class User{
private String name;
private String jpName;
private String qpName;
public String getName(){
return name;
}
public String getJPName(){
return jpName;
}
public String getQPName(){
return qpName;
}
}
public static class SearchKeyPosition{
public int start = -1;
public int end = -1;
public SearchKeyPosition(int start, int end){
this.start = start;
this.end = end;
}
}
}