[下载地址: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