列表按字母进行a-z排序,进行快速查找,同时在搜索框可以进行快速检索。最近在项目中需要这样的功能,于是就在网上找了一下,下面上效果图:
这里,我做了一个demo,效果如上图:
a-z列表快速查找功能和搜索关键字高亮demo下载地址
记得关联android v7支持包appcompat_v7
没有又懒得下的同学我这里上传了一个:
appcompat_v7下载
解析:
用到的几个类和布局:
- Cartype : 是数据实体类
- CharacterParser:是汉字转换为拼音的方法类
- ClearEditText:基础自EditText的自定义控件
- LoadingViewUtil:加载搜索失败视图的工具类
- MainActivity:主Activity
- PinyinComparator:Cartype对象比较器
- SideBar:右边a-z字母滑条
- SortAdapter:ListView适配器
布局: - activity_main.xml:主页面布局
- layout_lack.xml:查找无结果视图
- sort_item.xml:list的item视图
数据表: - arrays.xml:预先写入配置的数据
大体的思路就是:
- 显示:先使用PinyinComparator比较强给从arrays.xml获取的数据根据拼音进行排序。并把首字母解析出来进行显示,在SortAdapter适配器中适配不同的item视图显示列表。
- 滑动:自定义SideBar捕捉滑动事件,在滑动事件中调用ListView.setSelection(position)改变列表当前位置,并显示字母弹窗。
- 搜索:捕捉编辑框的输入事件,通过输入对数据表进行筛选匹配,再调用notifyDataSetChanged()刷新列表数据。
代码也是在网上找的,自己看了一下根据需求修改了一下,增加了关键词高亮的功能
这里是原文链接,看代码解析的看这里:
下面主要讲一下改动的地方:关键词高亮效果的实现。
能实现高亮文字效果的方法有2个:
- 使用富文本类型SpannableString
- 使用html语言描述
2种方法都能实现文字高亮的效果,考虑到实体类的可序列化的特性,我使用了第二种:
关键词高亮:编辑框输入发生变化对数据表进行筛选匹配的时候,对需要显示的字符串进行处理,通过添加< font color=”>标签来改变其热点颜色。
我们给编辑框设置了文字变化监听:
//根据输入框输入值的改变来过滤搜索
mClearEditText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表
filterData(s.toString());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
});
当产生输入的时候会调用filterData()方法:
/**
* 根据输入框中的值来过滤数据并更新ListView
* @param filterStr
*/
private void filterData(String filterStr){
List<CarType> filterDateList = new ArrayList<CarType>();
if(TextUtils.isEmpty(filterStr)){
for (CarType carType : SourceDateList) {
if (carType.getCar_title_html()!=null) {
carType.setCar_title_html(null);
}
}
filterDateList = SourceDateList;
}else{
filterDateList.clear();
for(CarType sortModel : SourceDateList){
String name = sortModel.getCar_title();
match(filterDateList, sortModel, filterStr);
}
}
if (filterDateList==null || filterDateList.size()==0) {
showin = LoadingViewUtil.showinlack(this, R.drawable.icon_theme_search, "没有搜索结果", showin);
}else{
LoadingViewUtil.showout(this, showin);
}
// 根据a-z进行排序
Collections.sort(filterDateList, pinyinComparator);
adapter.updateListView(filterDateList);
}
方法中,如果判断如果输入非空,则会调用match(…)方法来匹配所有符合匹配规则的项,我们的重点就是要定制这个方法来实现我们的需求,如下:
private void match(List<CarType> filterDateList,CarType sortModel, String filterStr) {
boolean isMatch = false;
/**
* 查找字符串拼音以输入串开头的匹配字符串
*/
String car_title = sortModel.getCar_title();
int sellingCount = matchText(sortModel,filterStr);
if (sellingCount!=0) {
isMatch = true;
sortModel.setCar_title_html("<font color='#f08519'><b>" + car_title.substring(0,sellingCount) + "</b></font>" + car_title.substring(sellingCount));
}
/**
* 查找字符串中间是否有可以直接匹配的字符串
*/
int index = car_title.toLowerCase().indexOf(filterStr.toLowerCase().toString());
int length = filterStr.length();
if (index != -1) {
isMatch = true;
sortModel.setCar_title_html(car_title.substring(0,index) + "<font color='#f08519'><b>" + filterStr + "</b></font>" + car_title.substring(index+length));
}
if (isMatch) {
filterDateList.add(sortModel);
}
}
我定制了两种匹配规则:
- 输入英文字母会把被匹配对象解析成汉语拼音进行匹配,比如:”huabanx”可以匹配”滑板鞋”
- 同时会把输入字符串与被匹配对象进行直接匹配,比如:”ban”可匹配”huabanx”
先处理第一种情况,这种情况比较特殊,就是我们要计算出匹配成功了多少个字,这样才能让这几个字高亮,所有定义了一个方法int matchText(CarType sortModel, String filterStr),传入匹配字符串和被匹配对象,返回匹配成功的字数,没有匹配成功返回0:
/**
* 匹配字符串
* @param sortModel
* @param filterStr
*/
private int matchText(CarType sortModel, String filterStr) {
int sellingcount = 0;
String name = sortModel.getCar_title();
String[] sellingArray = characterParser.getSellingArray(name);
for (String selling : sellingArray) {
if ("".equals(filterStr)) {
return sellingcount;
}
if (filterStr.startsWith(selling)) {
sellingcount++;
filterStr = filterStr.substring(selling.length(),filterStr.length());
}else if(selling.startsWith(filterStr)){
sellingcount++;
return sellingcount;
}else {
return 0;
}
}
return sellingcount;
}
得到了匹配成功的字数,在match方法中就可以设置高亮效果了:
//匹配成功字数不为0则设置高亮
int sellingCount = matchText(sortModel,filterStr);
if (sellingCount!=0) {
isMatch = true;
sortModel.setCar_title_html("<font color='#f08519'><b>" + car_title.substring(0,sellingCount) + "</b></font>" + car_title.substring(sellingCount));
}
设置高亮的方法其实很简单,就是通过font 标签来拼接字符串就ok了。
下面第二种匹配情况会把输入字符串与被匹配对象进行直接匹配,如果被匹配对象中出现匹配字符串则高亮,这种方式不要计算字数因为它和匹配字符串长度是一样的:
/**
* 查找字符串中间是否有可以直接匹配的字符串
*/
int index = car_title.toLowerCase().indexOf(filterStr.toLowerCase().toString());
int length = filterStr.length();
if (index != -1) {
isMatch = true;
sortModel.setCar_title_html(car_title.substring(0,index) + "<font color='#f08519'><b>" + filterStr + "</b></font>" + car_title.substring(index+length));
}
然后在Adapter中判断是否有高亮效果,来决定是否当成Html格式进行解析:
if (list.get(position).getCar_title_html()!=null) {
viewHolder.tvTitle.setText(Html.fromHtml(list.get(position).getCar_title_html()));
}else {
viewHolder.tvTitle.setText(list.get(position).getCar_title());
}
这样列表的查找项中就能动态地显示高亮效果了。