前言
利用Collator类可以轻松实现排序,但是我们可能有各种model都需要进行排序,这样就会有一个问题,如果单独为每个model写一段排序代码,代码重复量很大。
所以我打算写一个通用的工具,使用泛型+注解+反射的方式来解决。
注解
首先创建注解类
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) @Documented public @interface SortString { } 复制代码
这个注解的作用就是标识哪个字段或函数是用来排序。
工具类
然后是排序工具类,在我这个工具中排序规则是:中文 > 数字 > 英文,这是我们app的需求,大家可以根据自己的需求进行修改。
完整代码如下
public class SimplifiedChineseSorter { private static final String SORTINGREGEX = "[^\\p{L}\\p{N}]+|^(The|A|An)\\b"; private static final Collator stringComparator = Collator.getInstance(Locale.SIMPLIFIED_CHINESE); public static <T> List<T> sortByProvider(List<T> items, SortStringProvider<T> provider, boolean isIgnoreCase) { if (items == null || items.size() <= 0) { return null; } return sortList(items, provider, isIgnoreCase); } public static <T> List<T> sortByFieldName(List<T> items, String fieldName, boolean isIgnoreCase) { if (items == null || items.size() <= 0) { return null; } Field field = getSortStringField(items.get(0).getClass(), fieldName); DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(field); return sortList(items, provider, isIgnoreCase); } public static <T> List<T> sortByFieldAnnotation(List<T> items, boolean isIgnoreCase) { if (items == null || items.size() <= 0) { return null; } Field field = getSortStringField(items.get(0).getClass()); DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(field); return sortList(items, provider, isIgnoreCase); } public static <T> List<T> sortByMethodName(List<T> items, String methodName, boolean isIgnoreCase) { if (items == null || items.size() <= 0) { return null; } Method method = getSortStringMethod(items.get(0).getClass(), methodName); DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(method); return sortList(items, provider, isIgnoreCase); } public static <T> List<T> sortByMethodAnnotation(List<T> items, boolean isIgnoreCase) { if (items == null || items.size() <= 0) { return null; } Method method = getSortStringMethod(items.get(0).getClass()); DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(method); return sortList(items, provider, isIgnoreCase); } private static <T> List<T> sortList(List<T> items, final SortStringProvider<T> provider, final boolean isIgnoreCase) { if(provider == null){ return items; } final List<T> chinieseList = new ArrayList<T>(); final List<T> nonChineseList = new ArrayList<T>(); for (T item : items) { if (isChineseCharStart(format(provider.getSortString(item), isIgnoreCase))) { chinieseList.add(item); } else { nonChineseList.add(item); } } List<T> sortedChineseList = Ordering.from(new Comparator<T>() { @Override public int compare(T lhs, T rhs) { return stringComparator.compare(format(provider.getSortString(lhs), isIgnoreCase), format(provider.getSortString(rhs), isIgnoreCase)); } }).sortedCopy(chinieseList); List<T> sortedNonChineseList = Ordering.from(new Comparator<T>() { @Override public int compare(T lhs, T rhs) { return format(provider.getSortString(lhs), isIgnoreCase).compareTo(format(provider.getSortString(rhs), isIgnoreCase)); } }).sortedCopy(nonChineseList); sortedChineseList.addAll(sortedNonChineseList); return sortedChineseList; } public static <T> Comparator<T> getSortComparatorByProvider(final Class clazz, SortStringProvider<T> provider, boolean isIgnoreCase) { return getSortComparator(provider, isIgnoreCase); } public static <T> Comparator<T> getSortComparatorByFieldName(final Class clazz, final String fieldName, boolean isIgnoreCase) { Field field = getSortStringField(clazz, fieldName); DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(field); return getSortComparator(provider, isIgnoreCase); } public static <T> Comparator<T> getSortComparatorByFieldAnnotation(final Class clazz, boolean isIgnoreCase) { Field field = getSortStringField(clazz); DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(field); return getSortComparator(provider, isIgnoreCase); } public static <T> Comparator<T> getSortComparatorByMethodName(final Class clazz, final String methodName, boolean isIgnoreCase) { Method method = getSortStringMethod(clazz, methodName); DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(method); return getSortComparator(provider, isIgnoreCase); } public static <T> Comparator<T> getSortComparatorByMethodAnnotation(final Class clazz, boolean isIgnoreCase) { Method method = getSortStringMethod(clazz); DefualtSortStringProvider<T> provider = new DefualtSortStringProvider<T>(method); return getSortComparator(provider, isIgnoreCase); } private static <T> Comparator<T> getSortComparator(final SortStringProvider<T> provider, final boolean isIgnoreCase) { return new Comparator<T>() { @Override public int compare(final T left, final T right) { String leftStr = format(provider.getSortString(left), isIgnoreCase); String rightStr = format(provider.getSortString(right), isIgnoreCase); if (SimplifiedChineseSorter.isChineseCharStart(leftStr) && SimplifiedChineseSorter.isChineseCharStart(rightStr)) { return stringComparator.compare(leftStr, rightStr); } else { return ComparisonChain.start() .compareTrueFirst(SimplifiedChineseSorter.isChineseCharStart(leftStr), SimplifiedChineseSorter.isChineseCharStart(rightStr)) .compare(leftStr, rightStr, Ordering.natural().nullsFirst()) .result(); } } }; } public static boolean isChineseCharStart(String str) { return !str.matches("[A-Za-z0-9\"“”]+.*"); } private static <T> Field getSortStringField(Class<T> tClass) { Field[] fields = tClass.getDeclaredFields(); if (fields != null) { for (Field field : fields) { if (field.isAnnotationPresent(SortString.class) && field.getType() == String.class) { field.setAccessible(true); return field; } } } Class superClass = tClass.getSuperclass(); if(superClass != null && !superClass.equals(Object.class)){ return getSortStringField(superClass); } throw new RuntimeException("The model doesn't have a @SortString field or the type of @SortString field is not a String"); } private static <T> Field getSortStringField(Class<T> tClass, String sortFieldName) { Field field = null; try { field = tClass.getDeclaredField(sortFieldName); } catch (NoSuchFieldException e) { } finally { if (field != null && field.getType() == String.class) { field.setAccessible(true); return field; } Class superClass = tClass.getSuperclass(); if(superClass != null && !superClass.equals(Object.class)){ return getSortStringField(superClass, sortFieldName); } throw new RuntimeException("The model doesn't have a field named " + sortFieldName); } } private static <T> Method getSortStringMethod(Class<T> tClass) { Method[] methods = tClass.getDeclaredMethods(); if (methods != null) { for (Method method : methods) { if (method.isAnnotationPresent(SortString.class) && method.getReturnType() == String.class) { method.setAccessible(true); return method; } } } Class superClass = tClass.getSuperclass(); if(superClass != null && !superClass.equals(Object.class)){ return getSortStringMethod(superClass); } throw new RuntimeException("The model doesn't have a @SortString method or the returnning type of @SortString method is not a String"); } private static <T> Method getSortStringMethod(Class<T> tClass, String sortMethodName) { Method method = null; try { method = tClass.getDeclaredMethod(sortMethodName); } catch (NoSuchMethodException e) { } finally { if (method != null && method.getReturnType() == String.class) { method.setAccessible(true); return method; } Class superClass = tClass.getSuperclass(); if(superClass != null && !superClass.equals(Object.class)){ return getSortStringMethod(superClass, sortMethodName); } throw new RuntimeException("The model doesn't have a method named " + sortMethodName); } } private static String format(String data, boolean isIgnoreCase) { Pattern pattern = Pattern.compile(SORTINGREGEX, Pattern.CASE_INSENSITIVE); if(data == null){ return ""; } else if(isIgnoreCase){ return pattern.matcher(data.trim()).replaceAll("").toLowerCase(); } else{ return pattern.matcher(data.trim()).replaceAll(""); } } static class DefualtSortStringProvider<T> implements SortStringProvider<T>{ private Object orderBy; DefualtSortStringProvider(Object orderBy){ this.orderBy = orderBy; } public String getSortString(T obj){ try { if (orderBy instanceof Field) { return (String) ((Field) orderBy).get(obj); } else if (orderBy instanceof Method) { return (String) ((Method)orderBy).invoke(obj); } } catch (Exception e){ Log.e("SimplifiedChineseSorter", "getSortString", e); } return ""; } } public interface SortStringProvider<T>{ String getSortString(T obj); } } 复制代码
这个类比较大而全,提供了很多方法来进行排序,其中主要四个函数:
- sortByFieldName:通过给定的字段名对应的字段进行排序
- sortByFieldAnnotation:通过带有@SortString的字段进行排序
- sortByMethodName:通过给定的函数名对应的函数进行排序
- sortByMethodAnnotation:通过带有@SortString的函数进行排序
其中注意
- sortByMethodName要保证该类中没有同名函数,当然也可以加入验证函数参数签名来防止同名函数。
- 如果通过注释排序,如果类中有多个@SortString注释,只有第一个有效果。目前这个工具只针对单一维度排序,当然其实完全可以修改成多维度排序,可以增加一个优先级。不过我们需求没有这么复杂,所以就没有进行开发。
使用
使用很简单,只要在类中为那个用来排序的字段(或函数)加上@SortString注解即可,比如
public class Experience { @SortString public String name; public int publicTime; public String summy; ... } 复制代码
然后就可以用SimplifiedChineseSorter.sortByFieldAnnotation
对Experience列表进行排序即可。
当然也可以用SimplifiedChineseSorter.sortByFieldName
,但是相对来说使用注释更加方便易于维护,之所以提供sortByFieldName
这类函数,主要是用于一些我们没有权限修改的类(比如三方库中的类),这样就没办法添加注释,就可以通过sortByFieldName
这类函数来进行排序,也比较方便。