在前一篇博客里分享了通过字母索引查找地名或联系人,接下来再分享下通过搜索查找地名或联系人。


使用搜索查找地名,实际上就是对ListView中的数据进行过滤,将符合条件的数据项重新保存起来进行显示。Demo效果如下:


android recycler 右侧字母索引_地名联系人


android recycler 右侧字母索引_ide_02


android recycler 右侧字母索引_ide_03




关键代码包括:


1)检查输入文本框内容变动,调用适配器进行过滤


public void afterTextChanged(Editable s) {
        /*在搜索时隐藏右边字母索引*/
        if(s.length() == 0){
            layout.setVisibility(View.VISIBLE);
        }else{
            layout.setVisibility(View.GONE);
        }
        listAdapter.getFilter().filter(s.toString());
    }



2)自定义适配器,并

添加自定义的过滤选项

class ListAdapter extends BaseAdapter implements Filterable{

        private Context context;
        private List<String> list;
        private ShortNameFilter filter;

        public ListAdapter(Context context , List<String> list){
            this.context = context;
            this.list = list;

        }
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Object getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            if(convertView == null){
                convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null);
            }
            TextView textView = (TextView) convertView.findViewById(android.R.id.text1);
            textView.setText(list.get(position));
            return convertView;
        }

        @Override
        public Filter getFilter() {
            if(filter == null)
                filter = new ShortNameFilter(list);
            return filter;
        }

        private class ShortNameFilter extends Filter {
            private List<String> original;

            public ShortNameFilter(List<String> list){
                this.original = list;
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                if(constraint == null || constraint.length() == 0){
                    results.values = original;
                    results.count = original.size();
                }else{
                    List<String> fList = new ArrayList<>();
                    String constraintUpper = constraint.toString().toUpperCase();
                    char firstChar = constraintUpper.charAt(0);
                    /*搜索文本为字母,则获取城市名称的首字母缩写,然后是否包含搜索文本
                    * 否则直接使用城市名检查是否包含搜索文本,将符合条件的数据项重新保存显示*/
                    if(firstChar >= 'A' && firstChar <= 'Z'){
                        for(String name:original){
                            if(FirstLetterUtil.Char2Initial(name.charAt(0)) == firstChar){
                                if(FirstLetterUtil.getFirstLetter(name).contains(constraintUpper))
                                    fList.add(name);
                            }
                        }
                    }else {
                        for(String name:original){
                            if(name.contains(constraint))
                                fList.add(name);
                        }
                    }
                    results.values = fList;
                    results.count = fList.size();
                }
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                list = (List<String>) results.values;
                notifyDataSetChanged();
            }
        }
    }



3)显示过滤后选中的数据

<span >	</span>listView = (ListView) findViewById(R.id.listView);
        Collections.addAll(cityNames, Cities.mCitiesStrings);

        listAdapter = new ListAdapter(context,cityNames);
        listView.setAdapter(listAdapter);
        listView.setOnItemClickListener(itemClickListener);

    AdapterView.OnItemClickListener itemClickListener =  new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            String choice = (String) listAdapter.getItem(position);
            Log.d(TAG, "onItemClick.choice = " + choice);
            textView.setText(choice);
        }
    };


当搜索文本框输入字母时,需要将城市名选项转化成字母进行匹配,这里参考了一个工具类,该工具类将每个声母中第一个汉字的GB2312编码存起来作为参考样本,然后查找目标汉字的编码,与样本汉字进行比较,从而确定目标汉字的首字母。但有些汉字的GB2312编码查找出来得出的结果不正确,例如我所使用的地名中,“ 亳州 重庆 漯河 泸州 濮阳 衢州”的首字母都在Z部中,会导致结果有一些小小的差异,也算一个bug吧。


全部代码如下:

public class CityActivity extends Activity implements TextWatcher {

    private static final String TAG = "CityActivity";
    private Map<Character,Integer> map = new TreeMap<Character,Integer>();
    private List<String> cityNames = new ArrayList<String>();
    private Context context;
    private ListView listView;
    private ListAdapter listAdapter;
    private LinearLayout layout;
    private EditText searchName;
    private TextView textView;
    private CharSequence temp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_city);

        initView();
    }

    private void initView(){
        context = CityActivity.this;

        textView = (TextView) findViewById(R.id.city);
        searchName = (EditText) findViewById(R.id.search_name);
        searchName.addTextChangedListener(this);
        listView = (ListView) findViewById(R.id.listView);
        Collections.addAll(cityNames, Cities.mCitiesStrings);

        listAdapter = new ListAdapter(context,cityNames);
        listView.setAdapter(listAdapter);
        listView.setOnItemClickListener(itemClickListener);
        initMap(Cities.mCitiesStrings);

        layout = (LinearLayout) findViewById(R.id.letter_index);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        for(final char letter:map.keySet()){
            TextView tv = new TextView(context);
            tv.setText(String.valueOf(letter));
            tv.setTextColor(getResources().getColor(R.color.text_darker));
            tv.setGravity(Gravity.CENTER);
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String indexLetter = ((TextView) v).getText().toString();
                    for (int i = 0; i < layout.getChildCount(); i++) {
                        TextView tv = (TextView) layout.getChildAt(i);
                        tv.setTextColor(getResources().getColor(R.color.text_darker));
                    }
                    ((TextView) v).setTextColor(getResources().getColor(R.color.main_color));
                    Log.d(TAG, "onClick.letter = " + indexLetter);
                    int position = map.get(indexLetter.charAt(0)) != null ? map.get(indexLetter.charAt(0)) : 0;
                    listView.setSelection(position);
                }
            });
            layout.addView(tv);
        }
    }

    /*
    * 将城市名称首字母第一次出现的id存放在map中
    * */
    private void initMap(final String[] strings){
        char curChar = '0';
        char firstChar = '0';
        for(int i = 0; i < strings.length; i++){
            firstChar = FirstLetterUtil.Char2Initial(strings[i].charAt(0));
            if(firstChar != curChar){
                curChar = firstChar;
                if(firstChar == 'Z' && "亳州 重庆 漯河 泸州 濮阳 衢州".contains(strings[i])) {
                    Log.d(TAG, "initMap.index = " + i + " " + strings[i]);
                    continue;
                }
                if(map.get(curChar) == null)
                    map.put(curChar,i);
            }
        }
    }

    AdapterView.OnItemClickListener itemClickListener =  new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            String choice = (String) listAdapter.getItem(position);
            Log.d(TAG, "onItemClick.choice = " + choice);
            textView.setText(choice);
        }
    };

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
    }

    @Override
    public void afterTextChanged(Editable s) {
        /*在搜索时隐藏右边字母索引*/
        if(s.length() == 0){
            layout.setVisibility(View.VISIBLE);
        }else{
            layout.setVisibility(View.GONE);
        }
        listAdapter.getFilter().filter(s.toString());
    }

    class ListAdapter extends BaseAdapter implements Filterable{

        private Context context;
        private List<String> list;
        private ShortNameFilter filter;

        public ListAdapter(Context context , List<String> list){
            this.context = context;
            this.list = list;

        }
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Object getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            if(convertView == null){
                convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null);
            }
            TextView textView = (TextView) convertView.findViewById(android.R.id.text1);
            textView.setText(list.get(position));
            return convertView;
        }

        @Override
        public Filter getFilter() {
            if(filter == null)
                filter = new ShortNameFilter(list);
            return filter;
        }

        private class ShortNameFilter extends Filter {
            private List<String> original;

            public ShortNameFilter(List<String> list){
                this.original = list;
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                if(constraint == null || constraint.length() == 0){
                    results.values = original;
                    results.count = original.size();
                }else{
                    List<String> fList = new ArrayList<>();
                    String constraintUpper = constraint.toString().toUpperCase();
                    char firstChar = constraintUpper.charAt(0);
                    /*搜索文本为字母,则获取城市名称的首字母缩写,然后是否包含搜索文本
                    * 否则直接使用城市名检查是否包含搜索文本,将符合条件的数据项重新保存显示*/
                    if(firstChar >= 'A' && firstChar <= 'Z'){
                        for(String name:original){
                            if(FirstLetterUtil.Char2Initial(name.charAt(0)) == firstChar){
                                if(FirstLetterUtil.getFirstLetter(name).contains(constraintUpper))
                                    fList.add(name);
                            }
                        }
                    }else {
                        for(String name:original){
                            if(name.contains(constraint))
                                fList.add(name);
                        }
                    }
                    results.values = fList;
                    results.count = fList.size();
                }
                return results;
            }

            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                list = (List<String>) results.values;
                notifyDataSetChanged();
            }
        }
    }

}



首字母转换类代码如下:

package com.huiyu.hornsey.mylibrary.common.util;

/**
 * Created by hornsey on 2015/11/23.
 */
public class FirstLetterUtil {
    private static int BEGIN = 45217;
    private static int END = 63486;
    // 按照声母表示,这个表是在GB2312中的出现的第一个汉字,也就是说“啊”是代表首字母a的第一个汉字。
    // i, u, v都不做声母, 自定规则跟随前面的字母
    private static char[] chartable = {'啊', '芭', '擦', '搭', '蛾', '发', '噶', '哈',
            '哈', '击', '喀', '垃', '妈', '拿', '哦', '啪', '期', '然', '撒', '塌', '塌',
            '塌', '挖', '昔', '压', '匝',};
    // 二十六个字母区间对应二十七个端点
    // GB2312码汉字区间十进制表示
    private static int[] table = new int[27];
    // 对应首字母区间表
    private static char[] initialtable = "ABCDEFGHHJKLMNOPQRSTTTWXYZ".toCharArray();

    // 初始化
    static {
        for (int i = 0; i < 26; i++) {
            table[i] = gbValue(chartable[i]);// 得到GB2312码的首字母区间端点表,十进制。
        }
        table[26] = END;// 区间表结尾
    }
    /**
     * 根据一个包含汉字的字符串返回一个汉字拼音首字母的字符串 最重要的一个方法,思路如下:一个个字符读入、判断、输出
     */
    public static String getFirstLetter(String sourceStr) {
        String result = "";
        String str = sourceStr.toUpperCase();

        int StrLength = str.length();
        int i;
        try {
            for (i = 0; i < StrLength; i++) {
                result += Char2Initial(str.charAt(i));
            }
        } catch (Exception e) {
            result = "";
        }
        return result;
    }
    /**
     * 输入字符,得到他的声母,英文字母返回对应的大写字母,其他非简体汉字返回 '0'
     */
    public static char Char2Initial(char ch) {
        // 对英文字母的处理:直接返回
        if (ch >= 'a' && ch <= 'z') {
            return ch;
        }
        if (ch >= 'A' && ch <= 'Z') {
            return ch;
        }
        // 对非英文字母的处理:转化为首字母,然后判断是否在码表范围内,
        // 若不是,则直接返回。
        // 若是,则在码表内的进行判断。
        int gb = gbValue(ch);// 汉字转换首字母
        if ((gb < BEGIN) || (gb > END))// 在码表区间之前,直接返回
        {
            return ch;
        }
        int i;
        for (i = 0; i < 26; i++) {// 判断匹配码表区间,匹配到就break,判断区间形如“[,)”
            if ((gb >= table[i]) && (gb < table[i + 1])) {
                break;
            }
        }
        if (gb == END) {//补上GB2312区间最右端
            i = 25;
        }
        return initialtable[i]; // 在码表区间中,返回首字母
    }
    /**
     * 取出汉字的编码 cn 汉字
     */
    private static int gbValue(char ch) {// 将一个汉字(GB2312)转换为十进制表示。
        String str = new String();
        str += ch;
        try {
            byte[] bytes = str.getBytes("GB2312");
            if (bytes.length < 2) {
                return 0;
            }
            return (bytes[0] << 8 & 0xff00) + (bytes[1] & 0xff);
        } catch (Exception e) {
            return 0;
        }
    }

}