简介
本文灵感来源是经典的https://github.com/rockerhieu/emojicon,部分代码直接不要脸的 copy 了,在其基础上更美观好用,且自定义空间更大。
最终效果图
源码下载与使用方法
https://github.com/mabeijianxi/android-expression
实现思想
首先叫 UI 给我做好的表情图片,然后保存在本地的 drawable 下。
然后不管你写个脚本也好一个一个输入也好,把图片名称全部缓存到数组,一个数组代表一页表情,需要多少页,一页表情有多少个就看 UI 喜好了,当然你会发现我多给了一个空字符串占位,因为我想在那里写死一个删除按钮~~。
把其名字与其 id 存入一个 Map ,这样可以在后面用的时候增加一些效率。
把每一页数据都放入一个 GridView,GridView 的每个 Item 都是一个 TextView,然后把这些
GridView 加入到 ViewPager,如果这时候我们不做处理,效果应该是这样的:
我们复写一个 TextView ,利用 Spannable 动态把对应的文字替换为图片即可,结果就然后就成了这样(细心的同学会发现4和5的图小点个数不一样,图4是最新项目的,我去掉了老版表情,图5是以前的):
核心代码详解
封装的部分不就唠叨了,直接点开源码一眼就能看懂的,现在直接看看一些核心方法。
文字替换:
检查一段文字中是否有匹配我们定义的表情字样,如果匹配上就替换为对应的图片。
public static Spannable transformExoression(Context context, Spannable text, int emojiSize, int emojiAlignment, int textSize) { int textLength = text.length(); HashMap<Integer, Integer> SpanIndex = new HashMap<>(); ExpressionSpan[] oldSpans = text.getSpans(0, textLength, ExpressionSpan.class); for (int i = 0; i < oldSpans.length; i++) { SpanIndex.put(text.getSpanStart(oldSpans[i]), text.getSpanEnd(oldSpans[i])); } String PATTERN = "\\[jx](.*?)\\[/jx]"; Pattern p = Pattern.compile(PATTERN); Matcher m = p.matcher(text); while (m.find()) { Integer maybeEnd = SpanIndex.get(m.start()); if ( maybeEnd!=null&&maybeEnd.intValue()== m.end()) { continue; } String beferGroup = m.group(); Integer index = ExpressionCache.getAllExpressionTable().get(beferGroup); int id = -1; if (index!=null&&index>=0) { id = index; } else { String afterGroup = beferGroup.replace("[jx]", "").replace("[/jx]", ""); id = context.getResources().getIdentifier(afterGroup, "drawable", context.getPackageName()); } if (id <= 0) { continue; } text.setSpan(new ExpressionSpan(context, id, emojiSize, emojiAlignment, textSize), m.start(), m.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return text; }
首先我通过 text.getSpans 获取了本段文字里面所有的 ExpressionSpan ,也就是表情信息,一个 ExpressionSpan 就是一个表情,我们把每个表情的起始位置都存入到一个 HashMap ,这样做可以很大程度上曾加效率!可以看到我们后面用正则去图片标识,也就是[jx] 开头, [/jx] 结尾的文字,为什么前面我那样做可以增加效率呢?可以看到如果找到一个符合的字样,首先我获取了它的开始角标,然后放入刚刚那个 HashMap 里面寻找其对应的 value,如果这个 value 干好等于我们刚寻找的字样的结束角标那就说明这个表情其实根本就没变化,我们无需再次对它替换,这种方式在输入的时候非常实用,我也是在输入表情数量特别多产生卡顿后才想到的解决办法,这样做当你输入一个表情时只会对你当前这个表情做处理,其他字样虽然符合但是会被我们全 continue。能继续往下走的,都是我们需要做处理的,我们只需要把这个字符串去我们缓存 id 的 Map 里面找就可以获得对应的图片 id 了,也就是执行 ExpressionCache.getAllExpressionTable().get(beferGroup)。然后就可以构建一个类似 ImageSpan 的 Span了。
最近使用列表维护
/** * 添加最近使用表情 * * @param str */ public static void addRecentExpression(String str) { int endIndex = getEndIndex(); for (int i = 0; i < ExpressionCache.getRecentExpression().length; i++) {// 防止重复 if (ExpressionCache.getRecentExpression()[i] == str || i == ExpressionCache.getRecentExpression().length - 1) { // 最多一页 sort(i, str); return; } } sort(endIndex, str); } /** * 后移动一位,只保留最近的21个 * * @param endIndex * @param newStr */ private static void sort(int endIndex, String newStr) { for (int i = endIndex; i > 0; i--) { ExpressionCache.getRecentExpression()[i] = ExpressionCache.getRecentExpression()[i - 1]; } ExpressionCache.getRecentExpression()[0] = newStr; } /** * 得到最后一位有效位 * * @return */ private static int getEndIndex() { int lastStr = 0; for (int i = 0; i < ExpressionCache.getRecentExpression().length; i++) { if (ExpressionCache.getRecentExpression()[i] == null || i == ExpressionCache.getRecentExpression().length - 1) { lastStr = i; break; } } return lastStr; }
首先这个存储最近使用表情的容器用的是数组,所以没法确定其正常的有效长度,我第一步是获取有效长度,然后就是简单移位排序而已。只要每点击一次我们就检查下需不需从新排序。
总结
我擦已经差不多了不写了,就在这一刻,大学女神找我聊天。。。让我教她追另外一个男的。。,可以了。。最后贴下源码地址https://github.com/mabeijianxi/android-expression