先看下效果

一 、布局

<!-- 上面的搜索框 -->
<com.example.editablealphalist.widgget.ClearEditText
android:id="@+id/filter_edit"
android:layout_marginTop="5dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/search_bar_edit_selector"
android:drawableLeft="@drawable/search_bar_icon_normal"
android:hint="请输入关键字"
android:singleLine="true"
android:textSize="15sp" /> <FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" > <ListView
android:id="@+id/name_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:divider="@null" /> <!-- 中间显示的选中的大写首字母 -->
<TextView
android:id="@+id/dialog"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:background="@drawable/show_head_toast_bg"
android:gravity="center"
android:textColor="@color/font_color_selected"
android:textSize="30sp"
android:visibility="gone" /> <!-- 右侧供选择的列表 -->
<com.example.editablealphalist.widgget.SideBar
android:id="@+id/sidrbar"
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_gravity="right|center" />
</FrameLayout>

二、自定义控件

2.1  ClearEditText 带删除按钮的输入框

public class ClearEditText extends AppCompatEditText implements OnFocusChangeListener, TextWatcher {

    /**
* 删除按钮的引用
*/
private Drawable mClearDrawable; public ClearEditText(Context context) {
this(context, null);
} public ClearEditText(Context context, AttributeSet attrs) {
// 这里构造方法也很重要,不加这个很多属性不能再XML里面定义
this(context, attrs, android.R.attr.editTextStyle);
} public ClearEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
} private void init() {
// 获取EditText的DrawableRight,假如没有设置我们就使用默认的图片
mClearDrawable = getCompoundDrawables()[2];
if (mClearDrawable == null) {
mClearDrawable = getResources().getDrawable(R.drawable.emotionstore_progresscancelbtn);
}
mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight());
setClearIconVisible(false);
setOnFocusChangeListener(this);
addTextChangedListener(this);
} @Override
public boolean onTouchEvent(MotionEvent event) { if (getCompoundDrawables()[2] != null) { if (event.getAction() == MotionEvent.ACTION_UP) { boolean touchable = event.getX() > (getWidth() - getPaddingRight() - mClearDrawable.getIntrinsicWidth())
&& (event.getX() < ((getWidth() - getPaddingRight())));
if (touchable) { //选中了“删除”按钮 this.setText("");
}
}
}
return super.onTouchEvent(event);
} /**
* 当ClearEditText焦点发生变化的时候,判断里面字符串长度设置清除图标的显示与隐藏
*/
@Override
public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { setClearIconVisible(getText().length() > 0);
} else { setClearIconVisible(false);
}
} /**
* 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去
*
* @param visible
*/
protected void setClearIconVisible(boolean visible) { Drawable right = visible ? mClearDrawable : null;
setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], right, getCompoundDrawables()[3]);
} /**
* 当输入框里面内容发生变化的时候回调的方法
*/
@Override
public void onTextChanged(CharSequence s, int start, int count, int after) {
setClearIconVisible(s.length() > 0);
} @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
} @Override
public void afterTextChanged(Editable s) { }
}

2.2  SideBar 右侧首字母列表

/**
* 右侧首字母列表bar
* @author Jack
* @version 创建时间:2014-2-6 下午3:37:33
*/
public class SideBar extends View { /** 触摸事件 */
private OnTouchingLetterChangedListener onTouchingLetterChangedListener;
/** 26个字母 */
public static String[] characters = { "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z", "#" };
private int ifSelected = -1; // 选中
private Paint paint = new Paint();
private TextView mTextDialog; public void setTextView(TextView mTextDialog) {
this.mTextDialog = mTextDialog;
} public SideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
} public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
} public SideBar(Context context) {
super(context);
} /**
* 重写这个方法
*/
protected void onDraw(Canvas canvas) { super.onDraw(canvas);
// 获取焦点改变背景颜色.
int height = getHeight(); // 获取对应高度
int width = getWidth(); // 获取对应宽度
int singleHeight = height / characters.length; // 获取每一个字母的高度 for (int i = 0; i < characters.length; i++) { // 设置颜色
paint.setColor(Color.rgb(33, 65, 98));
// 设置字体
paint.setTypeface(Typeface.DEFAULT_BOLD);
// 设置抗锯齿
paint.setAntiAlias(true);
// 设置字体大小
paint.setTextSize(20);
// 选中的状态
if (i == ifSelected) { paint.setColor(Color.parseColor("#3399ff"));
paint.setFakeBoldText(true);
}
// x坐标等于中间-字符串宽度的一半.
float xPos = width / 2 - paint.measureText(characters[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(characters[i], xPos, yPos, paint);
paint.reset();// 重置画笔
}
} @Override
public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction();
final float y = event.getY();// 点击y坐标
final int oldSelected = ifSelected;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
final int selected = (int) (y / getHeight() * characters.length);// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数. switch (action) { case MotionEvent.ACTION_UP: setBackgroundDrawable(new ColorDrawable(0x00000000));
ifSelected = -1;//
invalidate();
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
break;
default: setBackgroundResource(R.drawable.sidebar_background);
if (oldSelected != selected) {
if (selected >= 0 && selected < characters.length) {
if (listener != null) {
listener.onTouchingLetterChanged(characters[selected]);
}
if (mTextDialog != null) {
mTextDialog.setText(characters[selected]);
mTextDialog.setVisibility(View.VISIBLE);
}
ifSelected = selected;
invalidate();
}
}
break;
}
return true;
} /**
* 向外公开的方法
* @param onTouchingLetterChangedListener
*/
public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener onTouchingLetterChangedListener) { this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
} /**
* 触碰右侧列表时的回调接口
*/
public interface OnTouchingLetterChangedListener {
public void onTouchingLetterChanged(String s);
}

三、事件处理

3.1 右侧字母栏滑动处理

     sideBar = (SideBar) findViewById(R.id.sidrbar);
dialog = (TextView) findViewById(R.id.dialog);
sideBar.setTextView(dialog);
// 设置右侧触摸监听
sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() { @Override
public void onTouchingLetterChanged(String s) {
// 该字母首次出现的位置
int position = adapter.getPositionForSection(s.charAt(0));
if (position != -1) {
sortListView.setSelection(position);
}
}
});

3.2 输入框事件处理

//查询框设置监听
mClearEditText = (ClearEditText) findViewById(R.id.filter_edit);
// 根据输入框输入值的改变来过滤搜索
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) {
}
});
/**
* 根据输入框中的值来过滤数据源,截取含有查询字符串的数据
*
* @param filterStr 查询字符串
*/
private void filterData(String filterStr) { List<SortModel> filterDataList = new ArrayList<SortModel>();
if (TextUtils.isEmpty(filterStr)) { filterDataList = sourceDataList;
} else { filterDataList.clear();
for (SortModel sortModel : sourceDataList) { String name = sortModel.getName();
/*if (name.indexOf(filterStr.toString()) != -1 || characterParser.getSelling(name).startsWith(filterStr.toString())) { filterDataList.add(sortModel);
}*/
if (name.indexOf(filterStr.toString()) != -1 || pinyin4JUtil.convertChineseToPinyin(name).startsWith(filterStr.toString())) { filterDataList.add(sortModel);
}
}
}
// 根据a-z进行排序
Collections.sort(filterDataList, pinyinComparator);
adapter.updateListView(filterDataList);
}

四、汉字转拼音及多音字处理

4.1 初始化转换格式

/**
* 获取拼音初始汉语拼音格式化实例
*
* @param withTone
* @return
*/
private HanyuPinyinOutputFormat getHanyuPinyinOutputFormat(boolean withTone) {
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
if (withTone) {
//直接用音标符
format.setToneType(HanyuPinyinToneType.WITH_TONE_MARK);// WITHOUT_TONE:无音标 (xing)
//format.setToneType(HanyuPinyinToneType.WITH_TONE_NUMBER);// WITH_TONE_NUMBER:1-4数字表示英标 (xing2)
//直接用ü (nü)
format.setVCharType(HanyuPinyinVCharType.WITH_U_UNICODE);//WITH_V:用v表示ü (nv) //WITH_U_AND_COLON:用”u:”表示ü (nu:)
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
} else {
//直接用音标符
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
format.setVCharType(HanyuPinyinVCharType.WITH_U_UNICODE);
format.setCaseType(HanyuPinyinCaseType.UPPERCASE);
}
return format;
}
    1. setToneType 设置音标的显示方式:

      • HanyuPinyinToneType.WITH_TONE_MARK:在拼音字母上显示音标,如“zhòng”
      • HanyuPinyinToneType.WITH_TONE_NUMBER:在拼音字符串后面通过数字显示,如“zhong4”
      • HanyuPinyinToneType.WITHOUT_TONE:不显示音标
    2. setCaseType 设置拼音大小写:

      • HanyuPinyinCaseType.LOWERCASE:返回的拼音为小写字母
      • HanyuPinyinCaseType.UPPERCASE:返回的拼音为大写字母
    3. setVCharType 设置拼音字母“ü”的显示方式 
      汉语拼音中的“ü”不能简单的通过英文来表示,所以需要单独定义“ü”的显示格式

      • HanyuPinyinVCharType.WITH_U_UNICODE:默认的显示方式,输出“ü”
      • HanyuPinyinVCharType.WITH_V:输出“v”
      • HanyuPinyinVCharType.WITH_U_AND_COLON:输出“u:”

4.2 转换成汉语拼音

HanyuPinyinOutputFormat defaultFormat = getHanyuPinyinOutputFormat(false);
String[] results = PinyinHelper.toHanyuPinyinStringArray(ch, defaultFormat);//ch是要转化的汉字,用char表示。如'重'
如果results中拼音个数如果大于1说明该汉字是多音字

4.3 多音字处理

4.3.1 获取py4j.txt中的数据转换成map形式

private HashMap<String, String> initDictionary(Context context) {
String fileName = "py4j.txt";
InputStreamReader inputReader = null;
BufferedReader bufferedReader = null;
HashMap<String, String> polyphoneMap = new HashMap<String, String>();
try {
inputReader = new InputStreamReader(context.getResources().getAssets().open(fileName), "UTF-8");
bufferedReader = new BufferedReader(inputReader);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
//Log.d(TAG,"======"+line);
if (line != null) {
String[] arr = line.split("#");
String pinyin = arr[0];
String chinese = arr[1];
if (chinese != null) {
String[] strs = chinese.split(" ");
List<String> list = Arrays.asList(strs);
pinyinMap.put(pinyin, list);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}

将py4j.txt保存到assets目录通过context.getResources().getAssets().open(fileName)打开,存放到字符流中。

4.3.2 判断多音字并找到最优的拼音

{
int len = results.length;
if (len == 1) { // 不是多音字
String[] pinyinArray = getHanyuStrings(true, ch);
pinyin.append(pinyinArray[0]);
} else if (results[0].equals(results[1])) { //非多音字 有多个音,取第一个
//pinyin.append(results[0]);
String[] pinyinArray = getHanyuStrings(true, ch);
pinyin.append(pinyinArray[0]);
} else { // 多音字
int length = chinese.length();
boolean flag = false;
String s = null;
List<String> keyList = null;
for (int x = 0; x < len; x++) {
String py = results[x];
keyList = pinyinMap.get(py.toLowerCase());
resultPos = x;
if (i + 3 <= length) { //后向匹配2个汉字 大西洋
s = chinese.substring(i, i + 3);
if (keyList != null && (keyList.contains(s))) {
flag = true;
break;
}
}
if (i + 2 <= length) { //后向匹配 1个汉字 大西
s = chinese.substring(i, i + 2);
if (keyList != null && (keyList.contains(s))) {
flag = true;
break;
}
}
if ((i - 2 >= 0) && (i + 1 <= length)) { // 前向匹配2个汉字 龙固大
s = chinese.substring(i - 2, i + 1);
if (keyList != null && (keyList.contains(s))) {
flag = true;
break;
}
}
if ((i - 1 >= 0) && (i + 1 <= length)) { // 前向匹配1个汉字 固大
s = chinese.substring(i - 1, i + 1);
if (keyList != null && (keyList.contains(s))) {
flag = true;
break;
}
}
if ((i - 1 >= 0) && (i + 2 <= length)) { //前向1个,后向1个 固大西
s = chinese.substring(i - 1, i + 2);
if (keyList != null && (keyList.contains(s))) {
flag = true;
break;
}
}
}
if (!flag) {//都没有找到,匹配默认的 读音 大
s = String.valueOf(ch);
for (int x = 0; x < len; x++) {
String py = results[x];
keyList = pinyinMap.get(py.toLowerCase());
if (keyList != null && (keyList.contains(s))) {
String[] pinyinArray = getHanyuStrings(true, ch);
pinyin.append(pinyinArray[x]);
break;
}
}
} else {
String[] pinyinArray = getHanyuStrings(true, ch);
pinyin.append(pinyinArray[resultPos]);
}
}
}

如果是多音字,则去匹配py4j.txt中的字符是否存在找到对应的多音字下标

 String[] pinyinArray = getHanyuStrings(true, ch);
pinyin.append(pinyinArray[resultPos]);
...
private String[] getHanyuStrings(boolean withTone, char ch) {
HanyuPinyinOutputFormat format = getHanyuPinyinOutputFormat(withTone);
String[] pinyinArray = new String[0];
try {
pinyinArray = PinyinHelper.toHanyuPinyinStringArray(ch, format);
} catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {
badHanyuPinyinOutputFormatCombination.printStackTrace();
}
return pinyinArray;
}

五、缺点

判断多音字存在缺陷,只能通过py4j.txt字库中查找,字库资源不完善。

https://github.com/MichealPan9999/chinesetopinyin

py4j汉语转拼音多音字处理的更多相关文章

  1. C# 汉语转拼音

    汉语转拼音或首字母 通常不少网站上有汉语转拼音功能,今天就小记下这段汉语转拼音的代码,自己测试ok,现把代码贴出来,以备日后使用: 效果 用法很简单后台使用到了两个类文件,一个是:ConvertHzT ...

  2. java:工具(汉语转拼音,压缩包,EXCEL,JFrame窗口和文件选择器,SFTP上传下载,FTP工具类,SSH)

    1.汉语转拼音: import net.sourceforge.pinyin4j.PinyinHelper; import net.sourceforge.pinyin4j.format.HanyuP ...

  3. java 汉语转拼音(全拼,首字母)

    import java.util.*; import net.sourceforge.pinyin4j.PinyinHelper;import net.sourceforge.pinyin4j.for ...

  4. JS汉语转拼音脚本

    测试代码: var anhui = "安徽"; //获得全拼 pinyin.getFullChars(anhui); //获得首拼 pinyin.getCamelChars(anh ...

  5. js汉语转拼音(全拼、首字母、拼音首字母)

    新建js文件first_alphabet.js // JavaScript Document // 汉字拼音首字母列表 本列表包含了20902个汉字,用于配合 ToChineseSpell //函数使 ...

  6. 汉语转拼音pinyin4j

    分享一个将汉语转成拼音的工具包:pinyin4j-2.5.0.jar,下载地址:http://download.csdn.net/detail/abc_key/7629141 使用例如以下代码 imp ...

  7. Java-汉字繁体拼音转换

    import com.github.stuxuhai.jpinyin.ChineseHelper; import com.github.stuxuhai.jpinyin.PinyinFormat; i ...

  8. C#编写的通过汉字得到拼音和五笔码

    public static class SpellAndWbConfig { #region 变量声明 // XML文件读取实例 /// <summary> /// XML文件读取实例 / ...

  9. .net 生成拼音码与五笔码

    首先加入配置文件: <?xml version="1.0" encoding="utf-8" ?> <CodeConfig> <S ...

随机推荐

  1. luogu P2387 [NOI2014]魔法森林

    传送门 这题似乎不好直接做,可以考虑按照\(a_i\)升序排序,然后依次加边更新答案 具体实现方法是用lct维护当前的树,这里需要维护链上最大的\(b_i\).每次加一条边,如果加完以后没有环直接加, ...

  2. python 的基础 学习 第三

    1,in ,not in 判断子元素是是否在原字符串(字典,列表,集合)中,主要是用在检测敏感字 print('a' in 'abcchhhhd') 有则返回True, print('j' in 'a ...

  3. jQuery的节点添加、删除、替换等操作

    //几种添加节点的方法 //$("p").append("<b>你好吗?</b>");//向p元素中追加<b> //$(&q ...

  4. Java生成短链接

    为什么要生成短链接 微博之所以要是使用短链接,主要是因为微博只允许发140 字,如果链接地址太长的话,那么发送的字数将大大减少. 那么使用短链接的作用有哪些呢?1.字数,2.统计,3.监控,4.过滤 ...

  5. cookie——创建、获取、删除

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  6. 浏览器修改或添加Cookie--chrome插件【edit this cookie】、【postman】

    需求背景:某天用浏览器请求某个 API 提示: {"RESULT_TYPE" : -1 , "STATE" : 401 , "RESULT_DESC& ...

  7. Java异常处理之try-with-resources

    Oracle官方文档: http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html 概 ...

  8. 设计模式C++学习笔记之十七(Chain of Responsibility责任链模式)

      17.1.解释 概念:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. main(),客户 IWom ...

  9. VS2015 代码片段整理

    1.什么是代码段? 将一段代码行提取出来,可以多次重复的使用.VS IDE提供对代码段的完整支持.使代码编写更快.更容易.更可靠. 2.系统默认代码段 对于开发人员的例行任务,Visual Studi ...

  10. Ubuntu 18.04使用sudo pip3报错

    在使用sudo pip3 install python库的时候出现如下警告: The directory '/home/lzhu/.cache/pip/http' or its parent dire ...