列表按字母进行a-z排序,进行快速查找,同时在搜索框可以进行快速检索。最近在项目中需要这样的功能,于是就在网上找了一下,下面上效果图:

android 名称 手动搜网 安卓手机全部搜索列表_android 名称 手动搜网

android 名称 手动搜网 安卓手机全部搜索列表_高亮_02

这里,我做了一个demo,效果如上图:
a-z列表快速查找功能和搜索关键字高亮demo下载地址

记得关联android v7支持包appcompat_v7

android 名称 手动搜网 安卓手机全部搜索列表_快速查询_03

没有又懒得下的同学我这里上传了一个:
appcompat_v7下载

解析:

用到的几个类和布局:

android 名称 手动搜网 安卓手机全部搜索列表_android_04

android 名称 手动搜网 安卓手机全部搜索列表_快速查询_05

android 名称 手动搜网 安卓手机全部搜索列表_a-z列表_06

  • 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);
        }
    }

我定制了两种匹配规则:

  1. 输入英文字母会把被匹配对象解析成汉语拼音进行匹配,比如:”huabanx”可以匹配”滑板鞋”
  2. 同时会把输入字符串与被匹配对象进行直接匹配,比如:”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());
        }

这样列表的查找项中就能动态地显示高亮效果了。