发布时间:2018-11-16
 
技术:Android
 

概述

最近要做一个类似微信的,在登录界面选择国家地区的功能,微信有中文汉字笔画排序以及中文拼音排序等几种方式,如下所示: 简体中文 拼音排序;繁体中文 笔画排序 ; 英文:字母排序

详细

一、需求描述

最近要做一个类似微信的,在登录界面选择国家地区的功能,微信有中文汉字笔画排序以及中文拼音排序等几种方式,如下所示:

微信:简体中文、拼音排序

微信:繁体中文、笔画排序

微信 英文 字母排序

二、实现效果

下面看看我的Demo实现的效果

简体中文、拼音排序

繁体中文、笔画排序

英文 字母排序

其他语言,显示其他语言,排序按照对应的英文名来排序

三、实现过程

在将这部分代码抽取出来,做了一个demo。项目结构如下所示:采用策略模式,分别有EnglishSortStrategy、PinyinSortStrategy、StrokeSortStrategy三种策略,分别表示英文排序策略、拼音排序策略、汉字笔画排序策略。

+ 拼音排序

通过pin4j工具类将汉字转换为拼音,然后按照拼音的字母进行排序。

+ 笔画排序

通过查找汉字笔画数据库,将每个汉字对应的笔画数、汉字、汉字对应的编码映射到map中,然后通过查询map找到每个汉字的笔画,最终按照笔画数目进行排序。

+ 英文排序

直接通过英文的字母顺序进行排序即可。

三种排序策略具体实现:

ISortStrategy.java 排序策略接口

package com.oyp.sort.strategy;
import android.content.Context;
import com.oyp.sort.adapter.CountryOrRegionAdapter;
import com.oyp.sort.bean.CountryOrRegion;
import java.util.List;
/**
* 排序的策略
*/
public interface ISortStrategy {
/**
* 获取排序过后的国家区域列表
*
* @param countryOrRegionList 待排序的国家区域列表
* @return 排序过后的国家区域列表
*/
List<CountryOrRegion> getSortedCountryOrRegionList(List<CountryOrRegion> countryOrRegionList);
/**
* 获取要展示的排序的title 拼音排序显示 首字母,笔画排序显示 几划
*
* @param countryOrRegion 封装的CountryOrRegion
* @return 要展示的排序的title
*/
String getSortTitle(CountryOrRegion countryOrRegion,Context context);
/**
* 根据ListView的当前位置获取排序标题 是否需要显示的对比值:
* - 拼音排序返回首字母的Char ascii值
* - 笔画排序返回首字母的汉字笔画数量
*
* @param list 数据List集合
* @param position 位置
* @return 是否需要显示的对比值
* - 拼音排序返回首字母的Char ascii值
* - 笔画排序返回首字母的汉字笔画数量
*/
int getSectionForPosition(List<CountryOrRegion> list, int position);
/**
* 获取第一次或者是最后一次 出现该排序标题的位置
*
* @param list 排序列表
* @param section 排序标题的值:拼音排序首字母的Char ascii值,笔画排序出现的笔画数
* @param isFirst 是否是第一次
* @return 第一次或者是最后一次 出现该排序标题的位置
*/
int getFirstOrLastPositionForSection(List<CountryOrRegion> list, int section, boolean isFirst);
/**
* 生成不同排序规则,侧边栏需要展示的列表
*
* @param mSourceDateList 数据源
* @return SideBar需要展示的列表
*/
String[] getSideBarSortShowItemArray(List<CountryOrRegion> mSourceDateList, Context context);
/**
* 返回 滑动到侧边栏的某一项时候,需要去拿这一项首次出现在listView中的位置
*
* @param adapter 适配器
* @param sideBarSortShowItem 滑动到侧边栏的某一项
* @return 这一项首次出现在listView中的位置
*/
int getSideBarSortSectionFirstShowPosition(CountryOrRegionAdapter adapter, String sideBarSortShowItem);
/**
* 获取拼音排序需要的拼音 或者 英文排序需要的英文,中文环境的返回拼音字母,其他环境的返回 国家码转换后的国家英文名
*
* @param countryOrRegion 国家地区封装类
* @return 拼音排序需要的拼音 或者 英文排序需要的英文
*/
String getPinyinOrEnglish(CountryOrRegion countryOrRegion);
/**
* 获取 拼音排序或者英文排序需要展示的 英文首字母
*
* @param pinyin 拼音排序需要的拼音 或者 英文排序需要的英文
* @return 需要展示的 英文首字母
*/
String getSortLetters(String pinyin);
/**
* 获取 笔画排序所需要的汉字笔画数量 中文环境的返回汉字笔画数量,其他环境的返回 -1
*
* @param name 国家名
* @param context 上下文
* @return 笔画排序所需要的汉字笔画数量
*/
int getStrokeCount(String name, Context context);
}

拼音排序策略 PinyinSortStrategy.java

package com.oyp.sort.strategy.impl;
import android.content.Context;
import com.oyp.sort.adapter.CountryOrRegionAdapter;
import com.oyp.sort.bean.CountryOrRegion;
import com.oyp.sort.strategy.ISortStrategy;
import com.oyp.sort.utils.pinyin.CharacterParser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 根据拼音来排序的策略
*/
public class PinyinSortStrategy implements ISortStrategy {
/**
* 获取排序过后的国家区域列表
*
* @param countryOrRegionList 待排序的国家区域列表
* @return 排序过后的国家区域列表
*/
@Override
public List<CountryOrRegion> getSortedCountryOrRegionList(List<CountryOrRegion> countryOrRegionList) {
// 根据a-z进行排序源数据
Collections.sort(countryOrRegionList, new Comparator<CountryOrRegion>() {
@Override
public int compare(CountryOrRegion o1, CountryOrRegion o2) {
if (o1.getSortLetters().equals("@")
|| o2.getSortLetters().equals("#")) {
return -1;
} else if (o1.getSortLetters().equals("#")
|| o2.getSortLetters().equals("@")) {
return 1;
} else {
// return o1.getSortLetters().compareTo(o2.getSortLetters());
return o1.getPinyinName().compareTo(o2.getPinyinName());
}
}
});
return countryOrRegionList;
}
@Override
public String getSortTitle(CountryOrRegion countryOrRegion,Context context) {
//拼音的 只需要展示 英文首字母即可
return countryOrRegion.getSortLetters();
}
@Override
public int getSectionForPosition(List<CountryOrRegion> list, int position) {
return list.get(position).getSortLetters().charAt(0);
}
/**
* 根据拼音排序标题的首字母的Char ascii值获取其 第一次或者是最后一次 出现该首字母的位置
*
* @param section 分类的首字母的Char ascii值
* @param isFirst 是否是第一次
* @return 出现该首字母的位置
*/
@Override
public int getFirstOrLastPositionForSection(List<CountryOrRegion> list, int section, boolean isFirst) {
int count = list.size();
//如果查找第一个位置,从0开始遍历
if (isFirst) {
for (int i = 0; i < count; i++) {
if (isSameASCII(list, section, i)) {
return i;
}
}
} else { //如果查找的是最后一个位置,从最后一个开始遍历
for (int i = count - 1; i > 0; i--) {
if (isSameASCII(list, section, i)) {
return i;
}
}
}
//如果没有找到 返回 -1
return -1;
}
/**
* 判断 分类的首字母的Char ascii值 是否和 列表中的位置的Char ascii值一样
*
* @param section 分类的首字母的Char ascii值
* @param position 列表中的位置
* @return 是否相同
*/
private boolean isSameASCII(List<CountryOrRegion> list, int section, int position) {
String sortStr = list.get(position).getSortLetters();
char firstChar = sortStr.toUpperCase().charAt(0);
return firstChar == section;
}
@Override
public String[] getSideBarSortShowItemArray(List<CountryOrRegion> mSourceDateList,Context context) {
List<String> list = new ArrayList<>();
int count = mSourceDateList.size();
for (int i = 0; i < count; i++) {
CountryOrRegion countryOrRegion = mSourceDateList.get(i);
String sortLetters = countryOrRegion.getSortLetters();
if (!list.contains(sortLetters)) {
list.add(sortLetters);
}
}
return list.toArray(new String[list.size()]);
}
/**
* 返回 滑动到侧边栏的某个字母,首次出现在listView中的位置
*
* @param adapter 适配器
* @param sideBarSortShowItem 滑动到侧边栏的某个字母
* @return 滑动到侧边栏的某个字母首次出现在listView中的位置
*/
@Override
public int getSideBarSortSectionFirstShowPosition(CountryOrRegionAdapter adapter, String sideBarSortShowItem) {
return adapter.getPositionForSection(sideBarSortShowItem.charAt(0));
}
/**
* 拼音排序的时候,获取汉字的拼音
*
* @param countryOrRegion 国家地区封装类
* @return 汉字的拼音
*/
@Override
public String getPinyinOrEnglish(CountryOrRegion countryOrRegion) {
return CharacterParser.getInstance().getSplitSelling(countryOrRegion.getName());
}
/**
* 获取 拼音排序需要展示的 英文首字母
*
* @param pinyin 拼音排序需要的拼音
* @return 需要展示的 英文首字母
*/
@Override
public String getSortLetters(String pinyin) {
String sortString = pinyin.substring(0, 1).toUpperCase();
// 正则表达式,判断首字母是否是英文字母
if (sortString.matches("[A-Z]")) {
return sortString.toUpperCase();
} else {
return "#";
}
}
/**
* 拼音排序不需要这个字段做比较,直接返回-1
*/
@Override
public int getStrokeCount(String name, Context context) {
return -1;
}
}

英文排序策略 EnglishSortStrategy.java 继承自拼音排序策略PinyinSortStrategy.java

package com.oyp.sort.strategy.impl;
import android.content.Context;
import android.util.Log;
import com.oyp.sort.bean.CountryOrRegion;
import com.oyp.sort.utils.pinyin.OtherLanguageCharacterParser;
/**
* 根据英文来排序的策略,实际上排序策略和拼音排序是一样的
*/
public class EnglishSortStrategy extends PinyinSortStrategy {
private static final String TAG = "EnglishSortStrategy";
/**
* 英文排序的时候,返回国家码转换后的国家英文名
*
* @param countryOrRegion 国家地区封装类
* @return 国家码转换后的国家英文名
*/
@Override
public String getPinyinOrEnglish(CountryOrRegion countryOrRegion) {
//获取其他国家的英文名: 国家码转换成国家英文名
String countryCode = countryOrRegion.getCountryCode();
String countryEnglishName = OtherLanguageCharacterParser.getInstance().getMapData(countryCode);
Log.d(TAG,"CountryOrRegion countryCode : " + countryCode + " ,countryEnglishName:" + countryEnglishName);
return countryEnglishName;
}
/**
* 获取 英文排序需要展示的 英文首字母 ,策略和拼音排序策略一样
*
* @param pinyin 英文排序需要的英文
* @return 需要展示的 英文首字母
*/
@Override
public String getSortLetters(String pinyin) {
return super.getSortLetters(pinyin);
}
/**
* 英文排序的 直接返回-1 ,策略和拼音排序策略一样
*/
@Override
public int getStrokeCount(String name, Context context) {
return super.getStrokeCount(name, context);
}
}

汉字笔画排序策略 StrokeSortStrategy.java

package com.oyp.sort.strategy.impl;
import android.content.Context;
import android.util.Log;
import com.oyp.sort.R;
import com.oyp.sort.adapter.CountryOrRegionAdapter;
import com.oyp.sort.bean.CountryOrRegion;
import com.oyp.sort.strategy.ISortStrategy;
import com.oyp.sort.utils.stroke.bean.Stroke;
import com.oyp.sort.utils.stroke.utils.StrokeUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
/**
* 根据汉字的笔画来排序的策略
*/
public class StrokeSortStrategy implements ISortStrategy {
private static final String TAG = "StrokeSortStrategy";
/**
* 获取排序过后的国家区域列表
*
* @param countryOrRegionList 待排序的国家区域列表
* @return 排序过后的国家区域列表
*/
@Override
public List<CountryOrRegion> getSortedCountryOrRegionList(List<CountryOrRegion> countryOrRegionList) {
//根据比较国家或地区的名称的笔画数量来进行排序
Collections.sort(countryOrRegionList, new Comparator<CountryOrRegion>() {
@Override
public int compare(CountryOrRegion c1, CountryOrRegion c2) {
int sum1 = c1.getStrokeCount();
int sum2 = c2.getStrokeCount();
return sum1 - sum2;
}
});
return countryOrRegionList;
}
@Override
public String getSortTitle(CountryOrRegion countryOrRegion,Context context) {
//返回笔划排序的笔画数量
return countryOrRegion.getStrokeCount() + context.getResources().getString(R.string.stroke_title);
}
@Override
public int getSectionForPosition(List<CountryOrRegion> list, int position) {
return list.get(position).getStrokeCount();
}
/**
* 根据笔画排序笔画数目的值获取其第一次或者是最后一次出现的位置
*
* @param section 笔画排序笔画数目
* @param isFirst 是否是第一次
* @return 第一次或者是最后一次出现指定笔画数目的值的位置
*/
@Override
public int getFirstOrLastPositionForSection(List<CountryOrRegion> list, int section, boolean isFirst) {
int count = list.size();
//如果查找第一个位置,从0开始遍历
if (isFirst) {
for (int i = 0; i < count; i++) {
if (isSameStrokeSum(list, section, i)) {
return i;
}
}
} else { //如果查找的是最后一个位置,从最后一个开始遍历
for (int i = count - 1; i > 0; i--) {
if (isSameStrokeSum(list, section, i)) {
return i;
}
}
}
//如果没有找到 返回 -1
return -1;
}
/**
* 判断 第一次出现该 笔画数目的位置 是否 和指定的位置一样
*
* @param section 笔画数目
* @param position 列表中的位置
* @return 是否相同
*/
private boolean isSameStrokeSum(List<CountryOrRegion> list, int section, int position) {
//获取指定位置的笔画数
int strokeCount = list.get(position).getStrokeCount();
return strokeCount == section;
}
@Override
public String[] getSideBarSortShowItemArray(List<CountryOrRegion> mSourceDateList, Context context) {
List<String> list = new ArrayList<>();
int count = mSourceDateList.size();
for (int i = 0; i < count; i++) {
CountryOrRegion countryOrRegion = mSourceDateList.get(i);
String strokeCount = getSortTitle(countryOrRegion,context);
if (!list.contains(strokeCount)) {
list.add(strokeCount);
}
}
return list.toArray(new String[list.size()]);
}
private static Pattern NUMBER_PATTERN = Pattern.compile("[^0-9]");
/**
* 返回 滑动到侧边栏的某个笔画描述,首次出现在listView中的位置
* 因为侧边栏显示的 类似于 "3划",因此需要将"划"字取出来,然后对比 "3" 第一次出现的postion
*
* @param adapter 适配器
* @param sideBarSortShowItem 滑动到侧边栏的某个笔画描述
* @return 滑动到侧边栏的某个笔画描述首次出现在listView中的位置
*/
@Override
public int getSideBarSortSectionFirstShowPosition(CountryOrRegionAdapter adapter, String sideBarSortShowItem) {
try {
//提取字符串中的数字
int strokeCount = Integer.parseInt(numberIntercept(sideBarSortShowItem));
//然后查询出 这个数字首次出现的时候
return adapter.getPositionForSection(strokeCount);
} catch (Exception e) {
Log.e(TAG,Log.getStackTraceString(e));
}
return 0;
}
/**
* 提取字符串中的数字
*
* @param numberString 包含数字的字符串
* @return 字符串中的数字
*/
public String numberIntercept(String numberString) {
return NUMBER_PATTERN.matcher(numberString).replaceAll("");
}
/**
* 笔画排序的时候,不需要拼音
*
* @param countryOrRegion 国家地区封装类
* @return 空字符串
*/
@Override
public String getPinyinOrEnglish(CountryOrRegion countryOrRegion) {
return "";
}
@Override
public String getSortLetters(String pinyin) {
return "#";
}
/**
* 获取 笔画排序所需要的汉字笔画数量 中文环境的返回汉字笔画数量,其他环境的返回 -1
*
* @param name 国家名
* @param context 上下文
* @return 笔画排序所需要的汉字笔画数量
*/
@Override
public int getStrokeCount(String name, Context context) {
//取出首字母上的字符code //返回指定索引处的字符(Unicode 代码点)。索引引用 char 值(Unicode 代码单元),其范围从 0到 length() - 1。
int codePoint = name.codePointAt(0);
//得到笔画的相关信息
StrokeUtils strokeUtils = StrokeUtils.newInstance(context);
//获取首个汉字
Stroke stroke = strokeUtils.getStroke(codePoint + "");
if (stroke != null) {
return Integer.parseInt(stroke.getStrokeSum());
}
return -1;
}
}

四、使用策略

初始化排序策略,根据语言自动切换不同的排序策略

  • 使用排序策略
    使用策略主要是在两个地方,

  • 初始化数据的时候,使用不同排序策略,生成不同的数据

数据排序的时候,使用不同策略对数据源进行排序

Adapter展示item标题的时候,根据不同策略展示不同的标题

Adapter做逻辑判断位置的时候,不同的策略返回不同的位置

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

Android实现中文汉字笔划(笔画)、中文拼音排序、英文排序的更多相关文章

  1. 简单测试--C#实现中文汉字转拼音首字母

    第一种: 这个是自己写的比较简单的实现方法,要做汉字转拼音首字母,首先应该有一个存储首字母的数组,然后将要转拼音码的汉字与每个首字母开头的第一个汉字即“最小”的汉字作比较,这里的最小指的是按拼音规则比 ...

  2. java获取中文汉字的所有拼音

    java获取中文汉字的所有拼音   中文汉字可能有很多读音,java中分别用1,2,3,4来区别,例如“作”字,就有三个读音,zuo1,zuo2,zuo4. java获取汉字读音拼音代码如下所示: S ...

  3. Android 正则表达式匹配汉字中文

    关于中文的正则表达式, 应该是^[\\u4E00-\\u9FFF]+$, 和论坛里常被人提起的^[\\u4E00-\\u9FA5]+$很接近需要注意的是论坛里说的^[\\u4E00-\\u9FA5]+ ...

  4. Android输入法扩展之外接键盘中文输入

    大家想不想要这样一台Android  Surface平板,看着就过瘾吧. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSVRsZWFrcw==/font/ ...

  5. Android 解压zip文件(支持中文)

    过了n多天后,当再次使用原先博客上写的那篇: Android 压缩解压zip文件 去做zip包的解压的时候,出现了原来没有发现的很多问题.首先是中文汉字问题,使用java的zip包不能很好的解决解压问 ...

  6. 用C#生成随机中文汉字验证码的基本原理

    前几天去申请免费QQ号码,突然发现申请表单中的验证码内容换成了中文,这叫真叫我大跌眼镜感到好笑,Moper上的猫儿们都大骂腾讯采用中文验证码.^_^  我不得不佩服腾讯为了防止目前网络上横行的QQ号码 ...

  7. php使用strlen()判断中文汉字字符串长度

    php使用strlen()判断中文汉字字符串长度 对于含有中文情况,此时可以采用: iconv_strlen($str,"UTF-8"); iconv_strlen 是无论是何种编 ...

  8. Oracle一个中文汉字占用几个字节

    Oracle 一个中文汉字 占用几个字节,要根据Oracle中字符集编码决定   查看oracle server端字符集 select userenv('language') from dual; 如 ...

  9. char 型变量中能不能存贮一个中文汉字,为什么?

    char类型可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char类型占2个字节(16比特),所以放一个 ...

随机推荐

  1. EF和LINQ 调用存储过程

    好久没有更新文章了,最近项目比较忙都没什么时间来分享最近的问题. 今天遇到一个超级傻逼的问题.C#中调用存储过程,自己code也10来年了,这应该是很简单的问题了.今天有2个新的api,一个只有1个参 ...

  2. Linear Regression总结

    转自:http://blog.csdn.net/dongtingzhizi/article/details/16884215 Linear Regression总结 作者:洞庭之子 微博:洞庭之子-B ...

  3. 使用word2vec训练中文词向量

    https://www.jianshu.com/p/87798bccee48 一.文本处理流程 通常我们文本处理流程如下: 1 对文本数据进行预处理:数据预处理,包括简繁体转换,去除xml符号,将单词 ...

  4. 我的四轴专用PID参数整定方法及原理---超长文慎入(转)

    给四轴调了好久的PID,总算是调好了,现分享PID参数整定的心得给大家,还请大家喷的时候手下留情. 首先说明一下,这篇文章的主旨并不是直接教你怎么调,而是告诉你这么调有什么道理,还要告诉大家为什么'只 ...

  5. Cognos11中关于CJAP第三方认证的相关配置

    cognos11同样适用于自定义java程序的第三方认证,而且在测试方面给了直观的测试接口,如下图所示 当用户配置好了自定义java程序的认证之后,程序会提示用户输入我们自己的认证库用户信息例如adm ...

  6. PHPCMS源码分析

    PHPCMS 一.模版引擎 如:调用单页面index.php?m=content&c=index&a=lists&catid=9.1.先获取到模版变量的值$template_l ...

  7. C/C++——程序的内存分配

    C/C++程序内存分配 一.预备知识-程序的内存分配 一个由c/C++编译的程序占用的内存分为下面几个部分 1.栈区(stack):由编译器自己主动分配释放 ,存放函数的參数值,局部变量的值等.其操作 ...

  8. ASP.NET使用包含文件,比如asp中用include的方法

    不是母板页,包含文件在ASP.NET中是如何使用的? 在网页里包含另一个网页(或文件)方法如下: 一.要包含的文件是css文件的话,用 <link type=text/css rel=style ...

  9. tail -f 和tail -F的区别

    http://flume.apache.org/FlumeUserGuide.html flume抓取 exec 的command 官网有如下建议:

  10. C++ 推断一棵二叉树是否对称

    一棵二叉树对称,就是说它假设以根为轴,翻转过去一样.例如以下图所看到的,以虚线为轴.把左边翻转到右边,各顶点及顶点中的值一一相应. watermark/2/text/aHR0cDovL2Jsb2cuY ...