[下载地址:http://vdisk.weibo.com/s/C3PYKjMB8Rgpp]
先上个效果图:
其次大致说说原理:
1,首先判断输入的字符,是否包含表情的文字,比如
这个表情对应的文件名为 emoji_1.png,它对应的文字描述: [可爱],如果我们在输出的是输出这么一句话:老婆,我想你了
。 那么我们对应的根本文字就是:老婆,我想你了[可爱]。
2,具体的转换过程就是用正则表达式比配文字中是否含有[xxx]这类的文字,如果有,那么我们就根据拿到的[xxx]找到它对应的资源文件id,当然这其中有一个关系表,看你怎么处理这个关系了。最后将其用SpannableString替换成文字,表面上显示有图片,其实TextView里的text依然是:老婆,我想你了[可爱]。这个过程明白么?
下面贴上DEMO工程的结构:
再贴上几个重要的类:
1. package com.example.facedemo;
2. import java.util.ArrayList;
3. import java.util.HashMap;
4. import java.util.List;
5. import java.util.regex.Matcher;
6. import java.util.regex.Pattern;
7. import android.content.Context;
8. import android.graphics.Bitmap;
9. import android.graphics.BitmapFactory;
10. import android.text.Spannable;
11. import android.text.SpannableString;
12. import android.text.TextUtils;
13. import android.text.style.ImageSpan;
14. import android.util.Log;
15. publicclass FaceConversionUtil {
16. privateint pageSize = 20;
17. privatestatic FaceConversionUtil mFaceConversionUtil;
18. private HashMap emojiMap = new HashMap();
19. private List emojis = new ArrayList();
20. public List> emojiLists = new ArrayList>();
21. private FaceConversionUtil() {
22. }
23. publicstatic FaceConversionUtil getInstace() {
24. if (mFaceConversionUtil == null) {
25. mFaceConversionUtil = new FaceConversionUtil();
26. }
27. return mFaceConversionUtil;
28. }
29. public SpannableString getExpressionString(Context context, String str) {
30. SpannableString spannableString = new SpannableString(str);
31. // 正则表达式比配字符串里是否含有表情,如: 我好[开心]啊
32. String zhengze = "\\[[^\\]]+\\]";
33. // 通过传入的正则表达式来生成一个pattern
34. Pattern sinaPatten = Pattern.compile(zhengze, Pattern.CASE_INSENSITIVE);
35. try {
36. dealexpression_r(context, spannableString, sinaPatten, 0);
37. } catch (Exception e) {
38. Log.e("dealExpression", e.getMessage());
39. }
40. return spannableString;
41. }
42. public SpannableString addFace(Context context, int imgId,
43. String spannableString) {
44. if (TextUtils.isEmpty(spannableString)) {
45. returnnull;
46. }
47. Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
48. imgId);
49. bitmap = Bitmap.createScaledBitmap(bitmap, 35, 35, true);
50. ImageSpan imageSpan = new ImageSpan(context, bitmap);
51. SpannableString spannable = new SpannableString(spannableString);
52. spannable.setSpan(imageSpan, 0, spannableString.length(),
53. Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
54. return spannable;
55. }
56. privatevoid dealexpression_r(Context context,
57. SpannableString spannableString, Pattern patten, int start)
58. throws Exception {
59. Matcher matcher = patten.matcher(spannableString);
60. while (matcher.find()) {
61. String key = matcher.group();
62. // 返回第一个字符的索引的文本匹配整个正则表达式,ture 则继续递归
63. if (matcher.start() < start) {
64. continue;
65. }
66. String value = emojiMap.get(key);
67. if (TextUtils.isEmpty(value)) {
68. continue;
69. }
70. int resId = context.getResources().getIdentifier(value, "drawable",
71. context.getPackageName());
72. // 通过上面匹配得到的字符串来生成图片资源id,下边的方法可用,但是你工程混淆的时候就有事了,你懂的。不是我介绍的重点
73. // Field field=R.drawable.class.getDeclaredField(value);
74. // int resId=Integer.parseInt(field.get(null).toString());
75. if (resId != 0) {
76. Bitmap bitmap = BitmapFactory.decodeResource(
77. context.getResources(), resId);
78. bitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, true);
79. // 通过图片资源id来得到bitmap,用一个ImageSpan来包装
80. ImageSpan imageSpan = new ImageSpan(bitmap);
81. // 计算该图片名字的长度,也就是要替换的字符串的长度
82. int end = matcher.start() + key.length();
83. // 将该图片替换字符串中规定的位置中
84. spannableString.setSpan(imageSpan, matcher.start(), end,
85. Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
86. if (end < spannableString.length()) {
87. // 如果整个字符串还未验证完,则继续。。
88. dealexpression_r(context, spannableString, patten, end);
89. }
90. break;
91. }
92. }
93. }
94. publicvoid getFileText(Context context) {
95. ParseData(FileUtils.getEmojiFile(context), context);
96. }
97. privatevoid ParseData(List data, Context context) {
98. if (data == null) {
99. return;
100. }
101. ChatEmoji emojEentry;
102. try {
103. for (String str : data) {
104. String[] text = str.split(",");
105. String fileName = text[0]
106. .substring(0, text[0].lastIndexOf("."));
107. emojiMap.put(text[1], fileName);
108. int resID = context.getResources().getIdentifier(fileName,
109. "drawable", context.getPackageName());
110. if (resID != 0) {
111. emojEentry = new ChatEmoji();
112. emojEentry.setId(resID);
113. emojEentry.setCharacter(text[1]);
114. emojEentry.setFaceName(fileName);
115. emojis.add(emojEentry);
116. }
117. }
118. int pageCount = (int) Math.ceil(emojis.size() / 20 + 0.1);
119. for (int i = 0; i < pageCount; i++) {
120. emojiLists.add(getData(i));
121. }
122. } catch (Exception e) {
123. e.printStackTrace();
124. }
125. }
126. private List getData(int page) {
127. int startIndex = page * pageSize;
128. int endIndex = startIndex + pageSize;
129. if (endIndex > emojis.size()) {
130. endIndex = emojis.size();
131. }
132. // 不这么写,会在viewpager加载中报集合操作异常,我也不知道为什么
133. List list = new ArrayList();
134. list.addAll(emojis.subList(startIndex, endIndex));
135. if (list.size() < pageSize) {
136. for (int i = list.size(); i < pageSize; i++) {
137. ChatEmoji object = new ChatEmoji();
138. list.add(object);
139. }
140. }
141. if (list.size() == pageSize) {
142. ChatEmoji object = new ChatEmoji();
143. object.setId(R.drawable.face_del_icon);
144. list.add(object);
145. }
146. return list;
147. }
148. }
package com.example.facedemo; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.ImageSpan; import android.util.Log; public class FaceConversionUtil { private int pageSize = 20; private static FaceConversionUtil mFaceConversionUtil; private HashMap emojiMap = new HashMap(); private List emojis = new ArrayList(); public List> emojiLists = new ArrayList>(); private FaceConversionUtil() { } public static FaceConversionUtil getInstace() { if (mFaceConversionUtil == null) { mFaceConversionUtil = new FaceConversionUtil(); } return mFaceConversionUtil; } public SpannableString getExpressionString(Context context, String str) { SpannableString spannableString = new SpannableString(str); // 正则表达式比配字符串里是否含有表情,如: 我好[开心]啊 String zhengze = "\\[[^\\]]+\\]"; // 通过传入的正则表达式来生成一个pattern Pattern sinaPatten = Pattern.compile(zhengze, Pattern.CASE_INSENSITIVE); try { dealexpression_r(context, spannableString, sinaPatten, 0); } catch (Exception e) { Log.e("dealExpression", e.getMessage()); } return spannableString; } public SpannableString addFace(Context context, int imgId, String spannableString) { if (TextUtils.isEmpty(spannableString)) { return null; } Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), imgId); bitmap = Bitmap.createScaledBitmap(bitmap, 35, 35, true); ImageSpan imageSpan = new ImageSpan(context, bitmap); SpannableString spannable = new SpannableString(spannableString); spannable.setSpan(imageSpan, 0, spannableString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return spannable; } private void dealexpression_r(Context context, SpannableString spannableString, Pattern patten, int start) throws Exception { Matcher matcher = patten.matcher(spannableString); while (matcher.find()) { String key = matcher.group(); // 返回第一个字符的索引的文本匹配整个正则表达式,ture 则继续递归 if (matcher.start() < start) { continue; } String value = emojiMap.get(key); if (TextUtils.isEmpty(value)) { continue; } int resId = context.getResources().getIdentifier(value, "drawable", context.getPackageName()); // 通过上面匹配得到的字符串来生成图片资源id,下边的方法可用,但是你工程混淆的时候就有事了,你懂的。不是我介绍的重点 // Field field=R.drawable.class.getDeclaredField(value); // int resId=Integer.parseInt(field.get(null).toString()); if (resId != 0) { Bitmap bitmap = BitmapFactory.decodeResource( context.getResources(), resId); bitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, true); // 通过图片资源id来得到bitmap,用一个ImageSpan来包装 ImageSpan imageSpan = new ImageSpan(bitmap); // 计算该图片名字的长度,也就是要替换的字符串的长度 int end = matcher.start() + key.length(); // 将该图片替换字符串中规定的位置中 spannableString.setSpan(imageSpan, matcher.start(), end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); if (end < spannableString.length()) { // 如果整个字符串还未验证完,则继续。。 dealexpression_r(context, spannableString, patten, end); } break; } } } public void getFileText(Context context) { ParseData(FileUtils.getEmojiFile(context), context); } private void ParseData(List data, Context context) { if (data == null) { return; } ChatEmoji emojEentry; try { for (String str : data) { String[] text = str.split(","); String fileName = text[0] .substring(0, text[0].lastIndexOf(".")); emojiMap.put(text[1], fileName); int resID = context.getResources().getIdentifier(fileName, "drawable", context.getPackageName()); if (resID != 0) { emojEentry = new ChatEmoji(); emojEentry.setId(resID); emojEentry.setCharacter(text[1]); emojEentry.setFaceName(fileName); emojis.add(emojEentry); } } int pageCount = (int) Math.ceil(emojis.size() / 20 + 0.1); for (int i = 0; i < pageCount; i++) { emojiLists.add(getData(i)); } } catch (Exception e) { e.printStackTrace(); } } private List getData(int page) { int startIndex = page * pageSize; int endIndex = startIndex + pageSize; if (endIndex > emojis.size()) { endIndex = emojis.size(); } // 不这么写,会在viewpager加载中报集合操作异常,我也不知道为什么 List list = new ArrayList(); list.addAll(emojis.subList(startIndex, endIndex)); if (list.size() < pageSize) { for (int i = list.size(); i < pageSize; i++) { ChatEmoji object = new ChatEmoji(); list.add(object); } } if (list.size() == pageSize) { ChatEmoji object = new ChatEmoji(); object.setId(R.drawable.face_del_icon); list.add(object); } return list; } }
下边是表情布局,带输入框的,这样可以多个地方使用,就不不会使用太多多余代码。
1. package com.example.facedemo;
2. import java.util.ArrayList;
3. import java.util.List;
4. import android.content.Context;
5. import android.graphics.Color;
6. import android.graphics.drawable.ColorDrawable;
7. import android.support.v4.view.ViewPager;
8. import android.support.v4.view.ViewPager.OnPageChangeListener;
9. import android.text.SpannableString;
10. import android.text.TextUtils;
11. import android.util.AttributeSet;
12. import android.view.Gravity;
13. import android.view.View;
14. import android.view.View.OnClickListener;
15. import android.view.ViewGroup;
16. import android.widget.AdapterView;
17. import android.widget.AdapterView.OnItemClickListener;
18. import android.widget.EditText;
19. import android.widget.GridView;
20. import android.widget.ImageView;
21. import android.widget.LinearLayout;
22. import android.widget.RelativeLayout;
23. publicclass FaceRelativeLayout extends RelativeLayout implements
24. OnItemClickListener, OnClickListener {
25. private Context context;
26. private OnCorpusSelectedListener mListener;
27. private ViewPager vp_face;
28. private ArrayList pageViews;
29. private LinearLayout layout_point;
30. private ArrayList pointViews;
31. private List> emojis;
32. private View view;
33. private EditText et_sendmessage;
34. private List faceAdapters;
35. privateint current = 0;
36. public FaceRelativeLayout(Context context) {
37. super(context);
38. this.context = context;
39. }
40. public FaceRelativeLayout(Context context, AttributeSet attrs) {
41. super(context, attrs);
42. this.context = context;
43. }
44. public FaceRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
45. super(context, attrs, defStyle);
46. this.context = context;
47. }
48. publicvoid setOnCorpusSelectedListener(OnCorpusSelectedListener listener) {
49. mListener = listener;
50. }
51. publicinterface OnCorpusSelectedListener {
52. void onCorpusSelected(ChatEmoji emoji);
53. void onCorpusDeleted();
54. }
55. @Override
56. protectedvoid onFinishInflate() {
57. super.onFinishInflate();
58. emojis = FaceConversionUtil.getInstace().emojiLists;
59. onCreate();
60. }
61. privatevoid onCreate() {
62. Init_View();
63. Init_viewPager();
64. Init_Point();
65. Init_Data();
66. }
67. @Override
68. publicvoid onClick(View v) {
69. switch (v.getId()) {
70. case R.id.btn_face:
71. // 隐藏表情选择框
72. if (view.getVisibility() == View.VISIBLE) {
73. view.setVisibility(View.GONE);
74. } else {
75. view.setVisibility(View.VISIBLE);
76. }
77. break;
78. case R.id.et_sendmessage:
79. // 隐藏表情选择框
80. if (view.getVisibility() == View.VISIBLE) {
81. view.setVisibility(View.GONE);
82. }
83. break;
84. }
85. }
86. publicboolean hideFaceView() {
87. // 隐藏表情选择框
88. if (view.getVisibility() == View.VISIBLE) {
89. view.setVisibility(View.GONE);
90. returntrue;
91. }
92. returnfalse;
93. }
94. privatevoid Init_View() {
95. vp_face = (ViewPager) findViewById(R.id.vp_contains);
96. et_sendmessage = (EditText) findViewById(R.id.et_sendmessage);
97. layout_point = (LinearLayout) findViewById(R.id.iv_image);
98. et_sendmessage.setOnClickListener(this);
99. findViewById(R.id.btn_face).setOnClickListener(this);
100. view = findViewById(R.id.ll_facechoose);
101. }
102. privatevoid Init_viewPager() {
103. pageViews = new ArrayList();
104. // 左侧添加空页
105. View nullView1 = new View(context);
106. // 设置透明背景
107. nullView1.setBackgroundColor(Color.TRANSPARENT);
108. pageViews.add(nullView1);
109. // 中间添加表情页
110. faceAdapters = new ArrayList();
111. for (int i = 0; i < emojis.size(); i++) {
112. GridView view = new GridView(context);
113. FaceAdapter adapter = new FaceAdapter(context, emojis.get(i));
114. view.setAdapter(adapter);
115. faceAdapters.add(adapter);
116. view.setOnItemClickListener(this);
117. view.setNumColumns(7);
118. view.setBackgroundColor(Color.TRANSPARENT);
119. view.setHorizontalSpacing(1);
120. view.setVerticalSpacing(1);
121. view.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
122. view.setCacheColorHint(0);
123. view.setPadding(5, 0, 5, 0);
124. view.setSelector(new ColorDrawable(Color.TRANSPARENT));
125. view.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
126. LayoutParams.WRAP_CONTENT));
127. view.setGravity(Gravity.CENTER);
128. pageViews.add(view);
129. }
130. // 右侧添加空页面
131. View nullView2 = new View(context);
132. // 设置透明背景
133. nullView2.setBackgroundColor(Color.TRANSPARENT);
134. pageViews.add(nullView2);
135. }
136. privatevoid Init_Point() {
137. pointViews = new ArrayList();
138. ImageView imageView;
139. for (int i = 0; i < pageViews.size(); i++) {
140. imageView = new ImageView(context);
141. imageView.setBackgroundResource(R.drawable.d1);
142. LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
143. new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT,
144. LayoutParams.WRAP_CONTENT));
145. layoutParams.leftMargin = 10;
146. layoutParams.rightMargin = 10;
147. layoutParams.width = 8;
148. layoutParams.height = 8;
149. layout_point.addView(imageView, layoutParams);
150. if (i == 0 || i == pageViews.size() - 1) {
151. imageView.setVisibility(View.GONE);
152. }
153. if (i == 1) {
154. imageView.setBackgroundResource(R.drawable.d2);
155. }
156. pointViews.add(imageView);
157. }
158. }
159. privatevoid Init_Data() {
160. vp_face.setAdapter(new ViewPagerAdapter(pageViews));
161. vp_face.setCurrentItem(1);
162. current = 0;
163. vp_face.setOnPageChangeListener(new OnPageChangeListener() {
164. @Override
165. publicvoid onPageSelected(int arg0) {
166. current = arg0 - 1;
167. // 描绘分页点
168. draw_Point(arg0);
169. // 如果是第一屏或者是最后一屏禁止滑动,其实这里实现的是如果滑动的是第一屏则跳转至第二屏,如果是最后一屏则跳转到倒数第二屏.
170. if (arg0 == pointViews.size() - 1 || arg0 == 0) {
171. if (arg0 == 0) {
172. vp_face.setCurrentItem(arg0 + 1);// 第二屏 会再次实现该回调方法实现跳转.
173. pointViews.get(1).setBackgroundResource(R.drawable.d2);
174. } else {
175. vp_face.setCurrentItem(arg0 - 1);// 倒数第二屏
176. pointViews.get(arg0 - 1).setBackgroundResource(
177. R.drawable.d2);
178. }
179. }
180. }
181. @Override
182. publicvoid onPageScrolled(int arg0, float arg1, int arg2) {
183. }
184. @Override
185. publicvoid onPageScrollStateChanged(int arg0) {
186. }
187. });
188. }
189. publicvoid draw_Point(int index) {
190. for (int i = 1; i < pointViews.size(); i++) {
191. if (index == i) {
192. pointViews.get(i).setBackgroundResource(R.drawable.d2);
193. } else {
194. pointViews.get(i).setBackgroundResource(R.drawable.d1);
195. }
196. }
197. }
198. @Override
199. publicvoidAdapterView