我的Android进阶之旅------>Android之AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
来源:互联网 发布:c 编程工具 编辑:程序博客网 时间:2024/04/28 19:12
我的Android进阶之旅------>Android之AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
前言:AutoCompleteTextView介绍:
设置下拉菜单于文本框之前的水平偏移。下拉菜单默认与文本框左对齐android:dropDownVerticalOffset
设置下拉菜单于文本框之前的垂直偏移。下拉菜单默认紧跟文本框
android:dropDownWidthsetDropDownWidth(int)设置下拉菜单的宽度android:popupBackgroundsetDropDownBackgroundResource(int)设置下拉菜单的背景
step1:新建项目TestQuickSearch,项目结构如下图
step2:设计应用的的UI界面 /layout/main.xml
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <RelativeLayout
- android:layout_width="fill_parent"
- android:layout_height="50dip"
- android:background="@drawable/title_bg_color"
- >
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="250dp"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_alignParentRight="true"
- >
- <SlidingDrawer
- android:id="@+id/slidingdrawer"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:handle="@+id/handle"
- android:content="@+id/search"
- android:orientation="horizontal"
- >
- <ImageViewandroid:id="@+id/handle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/search2"
- android:scaleType="fitCenter"
- />
- <AutoCompleteTextView
- android:id="@+id/search"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:hint="个股查询"
- android:singleLine="true"
- android:completionThreshold="1"
- />
- </SlidingDrawer>
- </LinearLayout>
- </RelativeLayout>
- </LinearLayout>
/res/drawable/title_bg_color.xml
- <?xmlversion="1.0"encoding="utf-8"?>
- <!--
- -->
- <shape xmlns:android="http://schemas.android.com/apk/res/android">
- <gradient
- android:startColor="#505050"
- android:centerColor="#202020"
- android:endColor="#000000"
- android:angle="270"/>
- <cornersandroid:radius="0dip"/>
- </shape>
AutoCompleteTextView具有输入提示的功能,但是它的这种提示不适合对股票列表的过滤,如果你玩过股票软件,就会知道只要输入股票名称的首字母或股票代码就会出现符合匹配的股票,这种过滤怎么实现呢?
还有个问题,汉字具有多音字,如何实现多音字的匹配,比如“长江证券”,无论你输入“cjzq”或者“zjzq”都会匹配到它,这都是需要解决的问题!匹配的关键在于重写BaseAdapter,让它实现Filterable接口,重写其中的getFilter(),如果你参照ArrayAdaper源码的话,写起来就会容易很多,事实上我就是这么做的。
step3:SearchAdapter.java
- package cn.roco.quicksearch.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 staticfinal 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 ArrayFilterextends 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);
- }
- elseif (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很多强大的功能在这里都用不到,所以被我统统去掉了,这样大大提高了匹配效率。
step4:再看一下PinYin4j.java:
- package cn.roco.quicksearch.util;
- 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);//获取首字母
- temp[i][j] = t[j];// //获取首字母,不需要再截取了
- }
- } 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!" };
- }
- }
- return paiLie(temp);// 为了去掉重复项,直接返回Set
- }
- /*
- * 求2维数组所有排列组合情况 比如:{{1,2},{3},{4},{5,6}}共有2中排列,为:1345,1346,2345,2346
- */
- private Set<String> paiLie(String[][] str) {
- int max = 1;
- for (int i =0; i < str.length; i++) {
- max *= str[i].length;
- }
- Set<String> result = new HashSet<String>();
- 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.add(s);
- }
- return result;
- }
- /**
- * @param args
- */
- public staticvoid 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,里面每一个汉字都对应着几个读音。
step5:PinyinHelper.java
- package cn.roco.quicksearch.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_to_simple_pinyin.txt";
- BufferedInputStream bis=new BufferedInputStream(PinyinHelper.class.getResourceAsStream(resourceName));
- properties=new Properties();
- properties.load(bis);
- } catch (FileNotFoundException ex){
- ex.printStackTrace();
- } catch (IOException ex){
- ex.printStackTrace();
- }
- }
- 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),直接存汉字的首字母就行了,这个另论!
step6:最后,看看使用方法:QuickSearchActivity.java
- package cn.roco.quicksearch;
- import android.app.Activity;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.AdapterView;
- import android.widget.AdapterView.OnItemClickListener;
- import android.widget.AutoCompleteTextView;
- import android.widget.SlidingDrawer;
- import cn.roco.quicksearch.util.SearchAdapter;
- public class QuickSearchActivityextends Activity {
- private staticfinal 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);
- }
- }
step7:部署应用到模拟器上,查看运行效果
项目源代码可以在以下地址下载:
- http://pan.baidu.com/share/link?shareid=412293&uk=805959799
- http://download.csdn.net/detail/qq446282412/6467519
- 我的Android进阶之旅------>Android之AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- 我的Android进阶之旅------>Android之AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字,Filterable的使用)
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)2
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)2
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示
- 我的Android进阶之旅------>Android用AutoCompleteTextView实现搜索历史记录提示
- android AutoCompleteTextView 实现输入提示,类似百度支持输入拼音提示中文(gray)
- android AutoCompleteTextView 实现输入提示,类似百度支持输入拼音提示中文(gray)
- android AutoCompleteTextView 实现输入提示,类似百度支持输入拼音提示中文(gray)
- android AutoCompleteTextView 实现输入提示,类似百度支持输入拼音提示中文(gray)
- Android 利用AutoCompleteTextView实现模糊搜索功能,搜索结果自动提示,识别拼音首字母并转汉字提示
- Android 利用AutoCompleteTextView实现模糊搜索功能,搜索结果自动提示,识别拼音首字母并转汉字提示
- cs使用zclip实现前端复制到剪贴板的功能
- 第十五周项目五电子词典
- 银行业务学习之道:信用卡的具体功能
- Qt之任务栏系统托盘图标
- mysql 事件
- 我的Android进阶之旅------>Android之AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- 切换上下文,阻塞,内存同步
- windows driver 的入门方法
- 【Objective-c 学习笔记】广播模式
- 使用Opengl绘制字体
- 按位与,或,异或 等算法
- 黑马程序员——Java基本开发环境搭建
- JAVA NIO 简介
- 大型网站技术架构