`

AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)

阅读更多
AutoCompleteTextView具有输入提示的功能,但是它的这种提示不适合对股票列表的过滤,如果你玩过股票软件,就会知道只要输入股票名称的首字母或股票代码就会出现符合匹配的股票,这种过滤怎么实现呢?
还有个问题,汉字具有多音字,如何实现多音字的匹配,比如“长江证券”,无论你输入“cjzq”或者“zjzq”都会匹配到它,这都是需要解决的问题!
匹配的关键在于重写BaseAdapter,让它实现Filterable接口,重写其中的getFilter(),如果你参照ArrayAdaper源码的话,写起来就会容易很多,事实上我就是这么做的,^o^
下面看一下源码:
package com.ql.util;

import java.util.*;

import android.content.Context;
import android.util.Log;
import android.view.*;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;

public class SearchAdapter<T> extends BaseAdapter implements Filterable {
	private List<T> mObjects;

	private List<Set<String>> pinyinList;//支持多音字,类似:{{z,c},{j},{z},{q,x}}的集合

	private final Object mLock = new Object();

	private int mResource;

	private int mFieldId = 0;

	private Context mContext;

	private ArrayList<T> mOriginalValues;
	private ArrayFilter mFilter;

	private LayoutInflater mInflater;

	public static final int ALL=-1;//全部
	private int maxMatch=10;//最多显示多少个可能选项
	//支持多音字
	public SearchAdapter(Context context,int textViewResourceId, T[] objects,int maxMatch) {
		// TODO Auto-generated constructor stub
		init(context, textViewResourceId, 0, Arrays.asList(objects));
		this.pinyinList = getHanziSpellList(objects);
		this.maxMatch=maxMatch;
	}
	
	public SearchAdapter(Context context,int textViewResourceId, List<T> objects,int maxMatch) {
		// TODO Auto-generated constructor stub
		init(context, textViewResourceId, 0, objects);
		this.pinyinList = getHanziSpellList(objects);
		this.maxMatch=maxMatch;
	}
	
	private void init(Context context, int resource, int textViewResourceId,List<T> objects) {
		mContext = context;
		mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		mResource = resource;
		mObjects = objects;
		mFieldId = textViewResourceId;
	}

	
	/**
	 * 获得汉字拼音首字母列表
	 */
	private List<Set<String>> getHanziSpellList(T[] hanzi){
		List<Set<String>> listSet=new ArrayList<Set<String>>();
		PinYin4j pinyin=new PinYin4j();
		for(int i=0;i<hanzi.length;i++){
			listSet.add(pinyin.getPinyin(hanzi[i].toString()));
		}
		return listSet;
	}
	/**
	 * 获得汉字拼音首字母列表
	 */
	private List<Set<String>> getHanziSpellList(List<T> hanzi){
        List<Set<String>> listSet=new ArrayList<Set<String>>();
        PinYin4j pinyin=new PinYin4j();
        for(int i=0;i<hanzi.size();i++){
        	listSet.add(pinyin.getPinyin(hanzi.get(i).toString()));
        }
        return listSet;
	}
	
	public int getCount() {
		return mObjects.size();
	}

	public T getItem(int position) {
		return mObjects.get(position);
	}

	public int getPosition(T item) {
		return mObjects.indexOf(item);
	}

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

	public View getView(int position, View convertView, ViewGroup parent) {
		return createViewFromResource(position, convertView, parent, mResource);
	}

	private View createViewFromResource(int position, View convertView,
			ViewGroup parent, int resource) {
		View view;
		TextView text;

		if (convertView == null) {
			view = mInflater.inflate(resource, parent, false);
		} else {
			view = convertView;
		}

		try {
			if (mFieldId == 0) {
				text = (TextView) view;
			} else {
				text = (TextView) view.findViewById(mFieldId);
			}
		} catch (ClassCastException e) {
			Log.e("ArrayAdapter",
					"You must supply a resource ID for a TextView");
			throw new IllegalStateException(
					"ArrayAdapter requires the resource ID to be a TextView", e);
		}

		text.setText(getItem(position).toString());

		return view;
	}

	public Filter getFilter() {
		if (mFilter == null) {
			mFilter = new ArrayFilter();
		}
		return mFilter;
	}

	private class ArrayFilter extends Filter {
		@Override
		protected FilterResults performFiltering(CharSequence prefix) {
			FilterResults results = new FilterResults();

			if (mOriginalValues == null) {
				synchronized (mLock) {
					mOriginalValues = new ArrayList<T>(mObjects);//
				}
			}

			if (prefix == null || prefix.length() == 0) {
				synchronized (mLock) {
//					ArrayList<T> list = new ArrayList<T>();//无
					ArrayList<T> list = new ArrayList<T>(mOriginalValues);//List<T>
					results.values = list;
					results.count = list.size();
				}
			} else {
				String prefixString = prefix.toString().toLowerCase();

				final ArrayList<T> hanzi = mOriginalValues;//汉字String
				final int count = hanzi.size();

				final Set<T> newValues = new HashSet<T>(count);//支持多音字,不重复

				for (int i = 0; i < count; i++) {
					final T value = hanzi.get(i);//汉字String
					final String valueText = value.toString().toLowerCase();//汉字String
					final Set<String> pinyinSet=pinyinList.get(i);//支持多音字,类似:{z,c}
					Iterator iterator= pinyinSet.iterator();//支持多音字
		        	while (iterator.hasNext()) {//支持多音字
		        		final String pinyin = iterator.next().toString().toLowerCase();//取出多音字里的一个字母
		        		
		        		if (pinyin.indexOf(prefixString)!=-1) {//任意匹配
		        			newValues.add(value);
		        		} 
		        		else if (valueText.indexOf(prefixString)!=-1) {//如果是汉字则直接添加
		        			newValues.add(value);
		        		}
		        	}
		        	if(maxMatch>0){//有数量限制
			        	if(newValues.size()>maxMatch-1){//不要太多
			        		break;
			        	}
		        	}
					
				}
				List<T> list=Set2List(newValues);//转成List
				results.values = list;
				results.count = list.size();
			}
			return results;
		}

		protected void publishResults(CharSequence constraint,FilterResults results) {

			mObjects = (List<T>) results.values;
			if (results.count > 0) {
				notifyDataSetChanged();
			} else {
				notifyDataSetInvalidated();
			}
		}
	}
	
	//List Set 相互转换
	public <T extends Object> Set<T> List2Set(List<T> tList) {   
		Set<T> tSet = new HashSet<T>(tList);   
		//TODO 具体实现看需求转换成不同的Set的子类。   
		return tSet;   
	}
	public <T extends Object> List<T> Set2List(Set<T> oSet) {   
		List<T> tList = new ArrayList<T>(oSet);   
		// TODO 需要在用到的时候另外写构造,根据需要生成List的对应子类。   
		return tList;   
	}
}

在源码当中使用了PinYin4j去获得汉字的首字母,由于可能是多音字,所以将每个汉字的拼音都放在了Set中。当然PinYin4j很多强大的功能在这里都用不到,所以被我统统去掉了,这样大大提高了匹配效率。再看一下PinYin4j.java:
package com.ql.util;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class PinYin4j {
	
	
	public PinYin4j(){
	}
	/**
	 * 字符串集合转换字符串(逗号分隔)
	 * 
	 * @author wyh
	 * @param stringSet
	 * @return
	 */
	public String makeStringByStringSet(Set<String> stringSet) {
		StringBuilder str = new StringBuilder();
		int i = 0;
		for (String s : stringSet) {
			if (i == stringSet.size() - 1) {
				str.append(s);
			} else {
				str.append(s + ",");
			}
			i++;
		}
		return str.toString().toLowerCase();
	}

	
	/**
	 * 获取拼音集合
	 * 
	 * @author wyh
	 * @param src
	 * @return Set<String>
	 */
	public Set<String> getPinyin(String src) {
			char[] srcChar;
			srcChar = src.toCharArray();

			//1:多少个汉字
			//2:每个汉字多少种读音
			String[][] temp = new String[src.length()][];
			for (int i = 0; i < srcChar.length; i++) {
				char c = srcChar[i];
				// 是中文或者a-z或者A-Z转换拼音(我的需求,是保留中文或者a-z或者A-Z)
				if (String.valueOf(c).matches("[\\u4E00-\\u9FA5]+")) {
						String[] t = PinyinHelper.getUnformattedHanyuPinyinStringArray(c);
						temp[i] = new String[t.length];
						for(int j=0;j<t.length;j++){
							temp[i][j]=t[j].substring(0,1);//获取首字母
						}
				} else if (((int) c >= 65 && (int) c <= 90)
						|| ((int) c >= 97 && (int) c <= 122)||c>=48&&c<=57||c==42) {//a-zA-Z0-9*
					temp[i] = new String[] { String.valueOf(srcChar[i]) };
				} else {
					temp[i] = new String[] {"null!"};
				}
				
			}
			String[] pingyinArray = paiLie(temp);
			return array2Set(pingyinArray);//为了去掉重复项
	}
	
	/*
	 * 求2维数组所有排列组合情况
	 * 比如:{{1,2},{3},{4},{5,6}}共有2中排列,为:1345,1346,2345,2346
	 */
	private String[] paiLie(String[][] str){
		int max=1;
		for(int i=0;i<str.length;i++){
			max*=str[i].length;
		}
		String[] result=new String[max];
		for(int i = 0; i < max; i++){
	            String s = "";
	            int temp = 1;      //注意这个temp的用法。
	            for(int j = 0; j < str.length; j++){
	                temp *= str[j].length;
	                s += str[j][i / (max / temp) % str[j].length];
	            }
	            result[i]=s;
	    }
		
		return result;
	}
	
	public static <T extends Object> Set<T> array2Set(T[] tArray) {   
        Set<T> tSet = new HashSet<T>(Arrays.asList(tArray));   
        // TODO 没有一步到位的方法,根据具体的作用,选择合适的Set的子类来转换。   
        return tSet;   
    } 
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//nongyeyinheng,nongyeyinhang,nongyeyinxing
		PinYin4j t=new PinYin4j();
		String str = "农业银行1234567890abcdefghijklmnopqrstuvwxyz*";
		System.out.println(t.makeStringByStringSet(t.getPinyin(str)));
	}

}

这只是一个工具类,它使用到了PinyinHelper,PinyinHelper是加载字库文件用的,字库文件为/assets/unicode_to_hanyu_pinyin.txt,里面每一个汉字都对应着几个读音。
package com.ql.util;

import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class PinyinHelper{
	private static PinyinHelper instance;
	private Properties properties = null;
	
    public static String[] getUnformattedHanyuPinyinStringArray(char ch){
        return getInstance().getHanyuPinyinStringArray(ch);
    }

    private PinyinHelper(){
    	initResource();
    }

    public static PinyinHelper getInstance(){
    	if(instance==null){
    		instance = new PinyinHelper();
    	}
        return instance;
    }
    
    private void initResource(){
        try{
        	final String resourceName = "/assets/unicode_to_hanyu_pinyin.txt";
//          final String resourceName = "/assets/unicode_py.ini";

        	properties=new Properties();
        	properties.load(getResourceInputStream(resourceName));

        } catch (FileNotFoundException ex){
            ex.printStackTrace();
        } catch (IOException ex){
            ex.printStackTrace();
        }
    }

    private BufferedInputStream getResourceInputStream(String resourceName){
        return new BufferedInputStream(PinyinHelper.class.getResourceAsStream(resourceName));
    }
    
    private String[] getHanyuPinyinStringArray(char ch){
        String pinyinRecord = getHanyuPinyinRecordFromChar(ch);

        if (null != pinyinRecord){
            int indexOfLeftBracket = pinyinRecord.indexOf(Field.LEFT_BRACKET);
            int indexOfRightBracket = pinyinRecord.lastIndexOf(Field.RIGHT_BRACKET);

            String stripedString = pinyinRecord.substring(indexOfLeftBracket
                    + Field.LEFT_BRACKET.length(), indexOfRightBracket);

            return stripedString.split(Field.COMMA);

        } else
            return null;
        
    }
    
    private String getHanyuPinyinRecordFromChar(char ch){
        int codePointOfChar = ch;
        String codepointHexStr = Integer.toHexString(codePointOfChar).toUpperCase();
        String foundRecord = properties.getProperty(codepointHexStr);
        return foundRecord;
    }

    class Field{
        static final String LEFT_BRACKET = "(";
        static final String RIGHT_BRACKET = ")";
        static final String COMMA = ",";
    }
    
}

至于解析字库,比如有一个汉字是这样的格式:4E01 (ding1,zheng1),保存 到String[]当中就是{"ding1","zheng1"}这样的。但是这样的话到了PinYin4j中还需要使用substring(0,1)截取首字母,效率有些低了,事实上文件中完全可以采用这样的格式存储:E01 (d,z),直接存汉字的首字母就行了,这个另论!

最后,看看使用方法:
public class QuickSearchActivity extends Activity {
	private static final String tag="QuickSearchActivity";
	private AutoCompleteTextView search ;
	private SlidingDrawer mDrawer;
	
	public SearchAdapter adapter=null;//
	//需要读取
	public String[] hanzi = new String[] {
			"长江证券100002","长江证券100001", "农业银行200001","工商银行300001" , 
			"招商银行100001", "建设银行100001", "中国银行100002", "华夏银行500002", 
			"上海银行100010", "浦发银行200009"
			};
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        initViews();
    }
    
    private void initViews(){
        search = (AutoCompleteTextView) findViewById(R.id.search);
        search.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> arg0, View arg1, int position,
					long id) {
				// TODO Auto-generated method stub
				Log.d(tag, "onItemClick:"+position);
			}
        	
		});
        
        search.setThreshold(1);
        
        adapter = new SearchAdapter<String>(this,
    			android.R.layout.simple_dropdown_item_1line, hanzi,SearchAdapter.ALL);//速度优先
        search.setAdapter(adapter);//
        
		mDrawer = (SlidingDrawer) findViewById(R.id.slidingdrawer);
		
	}
    
}




  • 大小: 18.1 KB
分享到:
评论
2 楼 ax003d 2014-02-08  
非常感谢!
下载的代码文件编码有点问题,楼主用的什么编码格式?
1 楼 jiayou_123 2014-01-14  
赞 赞 赞!!!

相关推荐

    AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)2

    本篇文章将深入探讨如何实现一个能够处理汉字拼音首字母,并且支持多音字的AutoCompleteTextView过滤提示功能。 首先,我们要了解多音字的概念。在中文里,有些字有多个读音,例如“还”可以读作“hái”或“huán...

    AutoCompleteTextView输入汉字拼音首字母实现过滤提示

    android API中的 AutoCompleteTextView组件只能匹配过滤纯英文或者纯汉字的 经过改进后可以输入 汉字拼音匹配汉字 类似于百度搜索 例如输入x 会匹配“迅雷”“迅速” 如果输入s 也可以匹配 “迅速”

    Android之AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)

    可以参考下面博客:我的Android进阶之旅------&gt;Android之AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字) (博客地址:http://blog.csdn.net/ouyang_peng/article/details/8826806)

    AutoCompleteTextView汉字和拼音关联

    通过以上步骤,我们就可以实现一个支持汉字和拼音查询的`AutoCompleteTextView`。`CityAdapter.java`中的具体实现细节,包括如何处理拼音、如何过滤和显示建议,都需要查看源代码才能详细分析。在实际开发中,可能还...

    AutoCompleteTextView中输入字母或者汉字通过Filterable实现自动筛选提醒Demo

    总结起来,通过实现 `Filterable` 接口,我们可以自定义 `AutoCompleteTextView` 的数据过滤逻辑,从而实现根据用户输入的字母或汉字动态显示匹配的提示项。在实际开发中,可以根据具体需求调整过滤算法和 UI 设计,...

    AutoCompleteTextView中文和拼音关联自动提示

    在Android开发中,`AutoCompleteTextView` 是一个非常实用的组件,它允许用户在输入时自动显示匹配的建议列表,从而提升用户体验。...在实际开发中,还可以根据需求进行更多的定制,如添加搜索历史记录、支持多音字等。

    AutoCompleteTextView自动提示问题

    AutoCompleteTextView是Android SDK提供的一种UI组件,用于在用户输入时提供下拉列表的自动提示功能,极大地提升了用户的输入体验。这个控件通常用于搜索框、地址输入等场景,可以根据用户输入的部分字符快速匹配出...

    AutoCompleteTextView自动提示输入文字信息

    本篇文章将深入探讨如何结合ListView来实现AutoCompleteTextView的自动提示功能。 1. **AutoCompleteTextView基本用法** AutoCompleteTextView继承自EditText,主要通过`setAdapter()`方法连接数据源,这个数据源...

    AutoCompleteTextView自动完成文字输入

    AutoCompleteTextView是Android SDK提供的一种用于输入文本时自动补全的视图组件,它扩展了EditText,能够根据用户输入的部分文字动态显示出匹配的建议列表。这个功能在许多应用中非常常见,例如搜索引擎、地址...

    使用AutoCompleteTextView实现自动匹配输入的内容

    AutoCompleteTextView是Android SDK提供的一种视图组件,用于在用户输入文本时提供下拉列表的自动补全功能。它能够极大地提升用户体验,特别是在用户需要从大量预定义选项中选择时。在开发移动应用,尤其是涉及搜索...

    AutoCompleteTextView 自动提示联系人信息

    在Android开发中,`AutoCompleteTextView` 是一个非常实用的组件,它允许用户在输入时自动显示匹配的建议列表,极大地提高了用户体验。本篇将详细讲解`AutoCompleteTextView`如何用于自动提示联系人信息,并结合从...

    Android仿百度谷歌自动提示——AutoCompleteTextView

    在Android开发中,提供用户友好的输入体验是至关重要的,其中一种常见的做法是实现自动提示功能,这在很多应用的搜索框中都能看到。本文将详细介绍如何在Android中仿照百度和谷歌的自动提示功能,利用`...

    Android AutoCompleteTextView自动提示输入

    Android AutoCompleteTextView自动提示输入。我博客网址: http://blog.csdn.net/qq_16064871。我写了很多博客都是有源码的,请多多关注。

    AutoCompleteTextViewDemo

    4. **过滤与匹配**:AutoCompleteTextView内部实现了过滤逻辑,当用户输入文本时,会触发适配器的`getFilter()`方法,该方法返回一个Filter对象,用于过滤数据并提供匹配的建议。 5. **显示建议列表**:一旦过滤...

    android 首字母搜索

    1. 数据结构设计:为了高效地进行首字母检索,可以创建一个HashMap或TreeMap,键为每个首字母(或拼音首字母),值为包含该首字母的项的集合。这样,我们可以在常数时间内完成查找。 2. 输入监听:在EditText中添加...

    AutoCompleteTextView+SQLite实现自动检索

    在Android开发中,`AutoCompleteTextView` 是一个非常实用的组件,它允许用户在输入时自动显示匹配的建议列表,极大地提高了用户体验。结合SQLite数据库,我们可以实现高效、动态的数据检索,为用户提供实时的搜索...

    使用AutoCompleteTextView登录界面的业务过滤

    - 首先,在布局XML文件中添加`AutoCompleteTextView`组件,并设置相应的属性,如输入限制、提示文字等。 - 在Java代码中,为`AutoCompleteTextView`设置数据源,通常是一个适配器(如ArrayAdapter或CursorAdapter...

    自定义AutoCompleteTextView下拉列表控件

    在Android开发中,`AutoCompleteTextView` 是一个非常常见的组件,它用于实现自动补全功能,通常用于输入框中提供用户可能输入的建议。在给定的标题“自定义AutoCompleteTextView下拉列表控件”中,我们可以理解为...

    AutoCompleteTextView 显示更多

    标题与描述均提到了“AutoCompleteTextView 显示更多”,这主要关注于如何优化AutoCompleteTextView在Android应用中的表现,特别是如何调整下拉建议列表的高度,以便在屏幕上显示更多的选项。以下将深入探讨这一主题...

Global site tag (gtag.js) - Google Analytics