Android表情文字EmotionText解析

来源:互联网 发布:沙盘类似软件 编辑:程序博客网 时间:2024/04/30 08:16

现在几乎所有的聊天类App都支持输入表情混合文字,但是Android的EditText通常只能输入文字。
今天就来实现一个可以显示表情混合文字的EditText和TextView。

老规矩,在节目开始之前,先来一个搞笑段子:
“大师,我有目标,有远见,心胸开阔,但为什么生活总不如意?”
大师把我带到一棵梅花树前。看着那暗暗吐露芬芳的梅花,我大有所悟。
“大师,你是要告诉我梅花香自苦寒来,只要坚持就能成功?”
“傻逼,梅前你说个屁啊!”

先来看一下实现的效果图
这里写图片描述

整体功能就两个:可以在EditText中输入表情和文字,接收到表情和文字混合的消息可以显示在TextView中。

先不看代码,先说一下原理,把原理弄明白了,再看代码就很清晰。

在这个工程中,使用的就是系统的EditText和TextView,所以可以看出来显示表情并不是我自定义的功能,而是本身就支持的。只不过通常EditText和TextView在显示文本时,我们输入的都是普通的String或者Charsequence,要显示表情,就不能输入普通的String了,而是输入一个特殊的东西,叫SpannableString。
SpannableString,从名字也能看出来,它就是String字符串,只不过它在显示的时候,会把字符串中的一部分替换成一张图片,而当你调用editText.getText()方法时,返回的还是String字符串。所以它就是一段字符串和一张图片的对应。在显示的时候是图片,getText()取出时是String。

这个原理解释清楚后,示例图所展示的功能就不难了。
首先是输入:
每点击一个表情,就构建一个SpannableString对象,这个SpannableString对象就是一个String和一张表情图片的对应,然后设置给EditText,EditText显示成表情图片;点击发送,取出EditText的内容,返回的就是String,然后把String发送出去,对方接受到String后,找到SpannableString对象中和表情图片对应的String,重新构建SpannableString对象,再显示在TextView中。

绕来绕去,就是String和表情图片的转来转去。

下面来讲代码实现:
1)、找24张表情图片,我就是从QQ中拿来的。
这里写图片描述
2)、在assets中新建emotions.xml文件

<emotions>    <emotion>        <code><![CDATA[[em:1:]]]></code>        <name>f001</name>    </emotion>    <emotion>        <code><![CDATA[[em:2:]]]></code>        <name>f002</name>    </emotion>    <emotion>        <code><![CDATA[[em:3:]]]></code>        <name>f003</name>    </emotion>    <emotion>        <code><![CDATA[[em:4:]]]></code>        <name>f004</name>    </emotion>    <emotion>        <code><![CDATA[[em:5:]]]></code>        <name>f005</name>    </emotion>    <emotion>        <code><![CDATA[[em:6:]]]></code>        <name>f006</name>    </emotion>    <emotion>        <code><![CDATA[[em:7:]]]></code>        <name>f007</name>    </emotion>    <emotion>        <code><![CDATA[[em:8:]]]></code>        <name>f008</name>    </emotion>    <emotion>        <code><![CDATA[[em:9:]]]></code>        <name>f009</name>    </emotion>    <emotion>        <code><![CDATA[[em:10:]]]></code>        <name>f010</name>    </emotion>    <emotion>        <code><![CDATA[[em:11:]]]></code>        <name>f011</name>    </emotion>    <emotion>        <code><![CDATA[[em:12:]]]></code>        <name>f012</name>    </emotion>    <emotion>        <code><![CDATA[[em:13:]]]></code>        <name>f013</name>    </emotion>    <emotion>        <code><![CDATA[[em:14:]]]></code>        <name>f014</name>    </emotion>    <emotion>        <code><![CDATA[[em:15:]]]></code>        <name>f015</name>    </emotion>    <emotion>        <code><![CDATA[[em:16:]]]></code>        <name>f016</name>    </emotion>    <emotion>        <code><![CDATA[[em:17:]]]></code>        <name>f017</name>    </emotion>    <emotion>        <code><![CDATA[[em:18:]]]></code>        <name>f018</name>    </emotion>    <emotion>        <code><![CDATA[[em:19:]]]></code>        <name>f019</name>    </emotion>    <emotion>        <code><![CDATA[[em:20:]]]></code>        <name>f020</name>    </emotion>    <emotion>        <code><![CDATA[[em:21:]]]></code>        <name>f021</name>    </emotion>    <emotion>        <code><![CDATA[[em:22:]]]></code>        <name>f022</name>    </emotion>    <emotion>        <code><![CDATA[[em:23:]]]></code>        <name>f023</name>    </emotion>    <emotion>        <code><![CDATA[[em:24:]]]></code>        <name>f024</name>    </emotion></emotions>

这里就是24个表情对应的String,这个保存在assets中,相当于一个配置文件。最红要用Java对象来保存这些。

3)、新建Java Bean:Emotion

public class Emotion {    private String code = null;    private String name = null;    public Emotion() {}    public Emotion(String code, String name) {        this.code = code;        this.name = name;    }    public String getCode() {        return code;    }    public void setCode(String code) {        this.code = code;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

4)、解析emotions.xml文件,保存成List

public static List<Emotion> getEmotions(InputStream inputStream) {        XmlPullParser parser = Xml.newPullParser();        int eventType = 0;        List<Emotion> emotions = null;        Emotion emotion = null;        try {            parser.setInput(inputStream, "UTF-8");            eventType = parser.getEventType();            while (eventType != XmlPullParser.END_DOCUMENT) {                switch (eventType) {                case XmlPullParser.START_DOCUMENT:                    emotions = new ArrayList<Emotion>();                    break;                case XmlPullParser.START_TAG:                    if ("emotion".equals(parser.getName())) {                        emotion = new Emotion();                    } else if ("code".equals(parser.getName())) {                        emotion.setCode(parser.nextText());                    } else if ("name".equals(parser.getName())) {                        emotion.setName(parser.nextText());                    }                    break;                case XmlPullParser.END_TAG:                    if ("emotion".equals(parser.getName())) {                        emotions.add(emotion);                        emotion = null;                    }                    break;                default:                    break;                }                eventType = parser.next();            }        } catch (Exception e) {            e.printStackTrace();        }        return emotions;    }

这个就是常规的xml解析,无所谓用哪种解析方法,反正返回正确就行。

5)、用GridView展示所有的表情

ArrayList<HashMap<String, Object>> items = getItems();SimpleAdapter saImageItems = new SimpleAdapter(this, items,        R.layout.emotion_item, new String[] { "itemImage" },        new int[] { R.id.iv_emotion });gvEmotions.setAdapter(saImageItems);

6)、点击表情时,构建SpannableString显示在EditText中(重点)

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {    Emotion emotion = emotions.get(position);    int cursor = etInput.getSelectionStart();    Field f;    try {        f = (Field) R.drawable.class.getDeclaredField(emotion.getName());        int j = f.getInt(R.drawable.class);        Drawable d = getResources().getDrawable(j);        int textSize = (int)etInput.getTextSize();        d.setBounds(0, 0, textSize, textSize);        String str = null;        int pos = position + 1;        if (pos < 10) {            str = "f00" + pos;        } else if (pos < 100) {            str = "f0" + pos;        } else {            str = "f" + pos;        }        SpannableString ss = new SpannableString(str);        ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BOTTOM);        ss.setSpan(span, 0, str.length(),                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);        etInput.getText().insert(cursor, ss);    } catch (Exception e) {        e.printStackTrace();    }}

这步是关键,首先根据点击的位置获取Emotion,然后根据name,用反射获取Drawable。
然后用Drawable创建ImageSpan,把ImageSpan设置给SpannableString。
最后调用editText.getText().insert(cursor, ss);设置显示内容。

详细解释:
24个表情是用f001-f024表示的,对应的24个图片名字也是f001.png-f024.png
根据我点击的位置,构建出具体的str和drawable。
str用来创建SpannableString。
SpannableString ss = new SpannableString(str);
drawable用来创建ImageSpan。
ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BOTTOM);
那这两个是怎么关联的呢?就是下一行代码:
ss.setSpan(span, 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
用span来对应ss中从0到str.length()这段字符串。
最后调用显示
editText.getText().insert(cursor, ss);
整个过程就是这样。

7)接受到表情文字混合的消息,显示在TextView中

String receiveString = receiveMessage();SpannableString spannableString = ExpressionUtil.getExpressionString(receiveString );textView.setText(spannableString );

就三部,关键是第二部getExpressionString方法是怎么实现的。

public static final String PATTEN_STR = "f0[0-9]{2}|f10[0-7]";public static SpannableString getExpressionString(Context context, String str, int textSize) {    SpannableString spannableString = new SpannableString(str);    Pattern sinaPatten = Pattern.compile(PATTEN_STR, Pattern.CASE_INSENSITIVE);    try {        dealExpression(context, spannableString, textSize, sinaPatten, 0);    } catch (Exception e) {        Log.e("dealExpression", e.getMessage());    }    return spannableString;}public static void dealExpression(Context context, SpannableString spannableString, int textSize, Pattern patten, int start) throws Exception {    Matcher matcher = patten.matcher(spannableString);    while (matcher.find()) {        String key = matcher.group();        if (matcher.start() < start) {            continue;        }        Field field = R.drawable.class.getDeclaredField(key);        int resId = field.getInt(R.drawable.class);        if (resId != 0) {            Drawable d = context.getResources().getDrawable(resId);            d.setBounds(0, 0, textSize, textSize);            ImageSpan imageSpan = new ImageSpan(d);            int end = matcher.start() + key.length();            spannableString.setSpan(imageSpan, matcher.start(), end,                    Spannable.SPAN_INCLUSIVE_EXCLUSIVE);            if (end < spannableString.length()) {                dealExpression(context, spannableString, textSize, patten, end);            }            break;        }    }}

我们所有的表情都是用f001-f024来对应的,所以,接受到消息String后,就要找出String中所有匹配f001-f024这种格式的,然后替换成对应的表情图片。

所以这段代码看似很多,其实就是对接受到的String,用正则表达式进行遍历匹配,每匹配到一个,就把匹配到的那一段用一个ImageSpan来对应,直到全部匹配完。
最后把最终的SpnanableString设置给TextView。

大工告成!!!

至此,整个实现的逻辑就讲完了,但是我的工程中远不止这些,还有很多边缘性的功能,但核心的东西都讲了。

最后,我把完整的工程代码放出来,需要的朋友下载吧。
http://download.csdn.net/detail/u011002668/9462085

本期节目就到这里,感谢大家的收看,下期再见。

(PS:最初写博客就是为了记录自己学习的知识点,但既然写了,还是想写好点,或许对别人能起到帮助的作用,本人水平有限,如果有不对的地方,欢迎指正。)

0 0
原创粉丝点击