Android聊天软件的开发(六)--表情
2014-06-20 17:49
686 查看
表情用于聊天对话的输入,实现的原理主要是:在EditText或TextView中,使用SpannableString,将特定字符串替换为图片。
首先,我们可以规定,表情的字符串为[**],图片名称为smiley_i.png(i为图片编号,从0开始)。
当我选择
表情时,EditText的实际输入为[憨笑],但是可以通过SpannableString,将[憨笑]替换显示为
。如下图,EditText的实际输入为“哈哈哈[憨笑][憨笑]”。
同样,将“哈哈哈[憨笑][憨笑]”在TextView显示,只需要将所有的表情字符串[**]替换为对应的表情图片即可。
注意:这里只有字符串--》表情的转换,不存在表情--》字符串的转换。
具体实现:
1. 表情字符串与图片资源的对应
表情字符串存储在arrays.xml的smiley_values数组中,其位置下标与对应表情的图片编号一致。比如[憨笑]的数组下标为40,则其表情图片名为smiley_40.png。然后可以根据smiley_40.png获得该图片的资源ID,如0x00000001。我们只要将[憨笑]和0x00000001一一对应,就可以实现表情的显示。
2. 自定义带表情输入的编辑框
FacialEditLayout.java:带表情输入的自定义编辑框布局。
FacialUtil.java:初始化数据,和添加,显示表情的工具类
facial_edit_layout.xml:自定义View的布局文件
3. 表情输入框的使用
在聊天界面中,需要使用表情输入框。使用比较简单,只要实现发送按钮的点击函数(setOnSendBtnClickListener)即可。同时,可以使用getMsgEditTxt获得消息内容,clearMsgEdit清空文本,isFacialShow判断表情框是否显示,以及hideFacialView隐藏表情框。
ChattingActivity.java:聊天界面Activity。主要贴出使用表情输入框的代码。
参考网址:http://blog.csdn.net/lnb333666/article/details/8546497
首页Android聊天软件的开发
首先,我们可以规定,表情的字符串为[**],图片名称为smiley_i.png(i为图片编号,从0开始)。
当我选择
表情时,EditText的实际输入为[憨笑],但是可以通过SpannableString,将[憨笑]替换显示为
。如下图,EditText的实际输入为“哈哈哈[憨笑][憨笑]”。
同样,将“哈哈哈[憨笑][憨笑]”在TextView显示,只需要将所有的表情字符串[**]替换为对应的表情图片即可。
注意:这里只有字符串--》表情的转换,不存在表情--》字符串的转换。
具体实现:
1. 表情字符串与图片资源的对应
表情字符串存储在arrays.xml的smiley_values数组中,其位置下标与对应表情的图片编号一致。比如[憨笑]的数组下标为40,则其表情图片名为smiley_40.png。然后可以根据smiley_40.png获得该图片的资源ID,如0x00000001。我们只要将[憨笑]和0x00000001一一对应,就可以实现表情的显示。
2. 自定义带表情输入的编辑框
FacialEditLayout.java:带表情输入的自定义编辑框布局。
public class FacialEditLayout extends LinearLayout implements OnItemClickListener, OnClickListener { private Context mContext; /** 发送按钮的监听事件 */ private OnSendBtnClickListener mSendBtnListener; /** 显示表情页的viewpager */ private ViewPager mFacialViewPager; /** 表情页界面集合 */ private ArrayList<View> mPageViews; /** 游标显示布局 */ private LinearLayout mCursorLayout; /** 游标点集合 */ private ArrayList<ImageView> mCursorViews; /** 表情集合 页+个 */ private List<List<SmileyFacial>> smileys; /** 表情区域 */ private View mFacialLayout; /** 打开或关闭表情的按钮 */ private ImageView mFacialEnable; /** 输入框 */ private EditText mMsgEdit; /** 发送按钮 */ private Button mSendBtn; /** 表情数据填充器 */ private List<FacialAdapter> mFacialAdapters; /** 当前表情页 */ private int current = 0; /** 表情的列数 */ private static final int COLUMNS = 7; /** 表情的行数 */ private static final int ROWS = 3; /** 游标的宽高 */ private static final int CURSOR_DIMEN = 12; /** 整个表情布局的高度,包含表情框,游标 */ private int mFacialLayoutHeight; /** 表情ViewPager的高度 */ private int mFacialViewPagerHeight; /** 每个表情的高度 */ private int mFacialHeight; /** 每个表情的宽度 */ private int mFacialWidth; public FacialEditLayout(Context context) { super(context); mContext = context; LayoutInflater.from(mContext) .inflate(R.layout.facial_edit_layout, this); } public FacialEditLayout(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; LayoutInflater.from(mContext) .inflate(R.layout.facial_edit_layout, this); } /***************************** 对外接口 start *********************************/ /** 发送按钮点击监听 */ public interface OnSendBtnClickListener { /** * @param input * 编辑框的输入 */ void onBtnClicked(String input); } /** 设置按钮点击监听器 */ public void setOnSendBtnClickListener(OnSendBtnClickListener listener) { mSendBtnListener = listener; } /** 获得编辑框的输入字串 */ public String getMsgEditTxt() { return mMsgEdit.getText().toString(); } /** 清空编辑框 */ public void clearMsgEdit() { mMsgEdit.setText(""); } /** * 判断表情框是否显示<BR/> * 一般用于重写返回按键时调用 */ public boolean isFacialShow() { return (mFacialLayout.getVisibility() == View.VISIBLE); } /** * 隐藏表情选择框<BR/> * 一般用于重写返回按键时调用 */ public void hideFacialView() { if (mFacialLayout.getVisibility() == View.VISIBLE) { mFacialEnable.setImageDrawable(getResources().getDrawable( R.drawable.facial_btn_normal)); mFacialLayout.setVisibility(View.GONE); } } /***************************** 对外接口 end *********************************/ @Override protected void onFinishInflate() { super.onFinishInflate(); onCreate(); } private void onCreate() { initView(); initViewPager(); initCursor(); initData(); } /** * 初始化控件 */ private void initView() { mFacialEnable = (ImageView) findViewById(R.id.facial_enable); mMsgEdit = (EditText) findViewById(R.id.facial_edit); mSendBtn = (Button) findViewById(R.id.facial_sendBtn); mFacialLayout = findViewById(R.id.facial_facialLayout); mFacialViewPager = (ViewPager) findViewById(R.id.facial_viewPager); mCursorLayout = (LinearLayout) findViewById(R.id.facial_cursor); mMsgEdit.setOnClickListener(this); mFacialEnable.setOnClickListener(this); mSendBtn.setOnClickListener(this); // 获得屏幕宽度 WindowManager wm = (WindowManager) getContext().getSystemService( Context.WINDOW_SERVICE); int screenWidth = wm.getDefaultDisplay().getWidth(); // 每个表情的宽高 mFacialWidth = screenWidth / COLUMNS - 1; mFacialHeight = mFacialWidth; // 表情框高度 mFacialViewPagerHeight = mFacialHeight * ROWS + 20; // 整个表情布局高度 mFacialLayoutHeight = mFacialViewPagerHeight + CURSOR_DIMEN + 30; RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mFacialViewPager .getLayoutParams(); lp.height = mFacialViewPagerHeight; mFacialViewPager.setLayoutParams(lp); LinearLayout.LayoutParams lp1 = (LayoutParams) mFacialLayout .getLayoutParams(); lp1.height = mFacialLayoutHeight; mFacialLayout.setLayoutParams(lp1); smileys = FacialUtils.getInstace().getFacialData(); } /** * 初始化显示表情的viewpager */ private void initViewPager() { mPageViews = new ArrayList<View>(); // 左侧添加空页 View nullView1 = new View(mContext); // 设置透明背景 nullView1.setBackgroundColor(Color.TRANSPARENT); mPageViews.add(nullView1); // 中间添加表情页 mFacialAdapters = new ArrayList<FacialAdapter>(); for (int i = 0; i < smileys.size(); i++) { GridView view = new GridView(mContext); FacialAdapter adapter = new FacialAdapter(mContext, smileys.get(i), mFacialWidth, mFacialWidth); view.setAdapter(adapter); mFacialAdapters.add(adapter); view.setOnItemClickListener(this); view.setNumColumns(COLUMNS);// 列数 view.setBackgroundColor(Color.TRANSPARENT); view.setHorizontalSpacing(1); view.setVerticalSpacing(1); view.setStretchMode(GridView.STRETCH_COLUMN_WIDTH); view.setCacheColorHint(0); view.setPadding(5, 0, 5, 0); view.setSelector(new ColorDrawable(Color.TRANSPARENT)); view.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); view.setGravity(Gravity.CENTER); mPageViews.add(view); } // 右侧添加空页面 View nullView2 = new View(mContext); // 设置透明背景 nullView2.setBackgroundColor(Color.TRANSPARENT); mPageViews.add(nullView2); } /** * 初始化游标 */ private void initCursor() { mCursorViews = new ArrayList<ImageView>(); ImageView imageView; for (int i = 0; i < mPageViews.size(); i++) { imageView = new ImageView(mContext); imageView.setBackgroundResource(R.drawable.facial_cursor_normal); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); layoutParams.leftMargin = 10; layoutParams.rightMargin = 10; layoutParams.width = CURSOR_DIMEN; layoutParams.height = CURSOR_DIMEN; mCursorLayout.addView(imageView, layoutParams); if (i == 0 || i == mPageViews.size() - 1) { imageView.setVisibility(View.GONE); } if (i == 1) { imageView .setBackgroundResource(R.drawable.facial_cursor_selected); } mCursorViews.add(imageView); } } /** * 填充数据 */ private void initData() { mFacialViewPager.setAdapter(new ViewPagerAdapter(mPageViews)); mFacialViewPager.setCurrentItem(1); current = 0; mFacialViewPager.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int position) { current = position - 1; // 描绘分页点 drawCursor(position); // 如果是第一屏或者是最后一屏禁止滑动,其实这里实现的是如果滑动的是第一屏则跳转至第二屏,如果是最后一屏则跳转到倒数第二屏. if (position == mCursorViews.size() - 1 || position == 0) { if (position == 0) { mFacialViewPager.setCurrentItem(position + 1);// 第二屏 // 会再次实现该回调方法实现跳转. mCursorViews.get(1).setBackgroundResource( R.drawable.facial_cursor_selected); } else { mFacialViewPager.setCurrentItem(position - 1);// 倒数第二屏 mCursorViews.get(position - 1).setBackgroundResource( R.drawable.facial_cursor_selected); } } } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }); } /** * 绘制游标背景 */ private void drawCursor(int index) { for (int i = 1; i < mCursorViews.size(); i++) { if (index == i) { mCursorViews.get(i).setBackgroundResource( R.drawable.facial_cursor_selected); } else { mCursorViews.get(i).setBackgroundResource( R.drawable.facial_cursor_normal); } } } /** 隐藏软键盘 */ private void hideKeyBoard() { if (mContext != null) { ((InputMethodManager) mContext .getSystemService(Context.INPUT_METHOD_SERVICE)) .hideSoftInputFromWindow(mMsgEdit.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.facial_enable:// 表情选择 /* * 让编辑框同时获得焦点 否则,如果先点击表情框,需要两次点击编辑框,表情框才会消失。 * 因为第一次点击,让编辑框获得焦点,然后才能监听点击操作 */ mMsgEdit.setFocusable(true); mMsgEdit.setFocusableInTouchMode(true); mMsgEdit.requestFocus(); // 显示或隐藏表情选择框 if (mFacialLayout.getVisibility() == View.VISIBLE) { mFacialEnable.setImageDrawable(getResources().getDrawable( R.drawable.facial_btn_normal)); mFacialLayout.setVisibility(View.GONE); } else { mFacialEnable.setImageDrawable(getResources().getDrawable( R.drawable.facial_btn_enable)); // 隐藏软键盘 hideKeyBoard(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } mFacialLayout.setVisibility(View.VISIBLE); } break; case R.id.facial_edit:// 编辑框 // 隐藏表情选择框 if (mFacialLayout.getVisibility() == View.VISIBLE) { mFacialEnable.setImageDrawable(getResources().getDrawable( R.drawable.facial_btn_normal)); mFacialLayout.setVisibility(View.GONE); } break; case R.id.facial_sendBtn:// 发送按钮 String input = mMsgEdit.getText().toString(); if (input.isEmpty()) return; if (mSendBtnListener != null) { // 调用按钮监听 mSendBtnListener.onBtnClicked(input); } break; } } /** * 监听表情选择 */ @Override public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) { SmileyFacial smiley = (SmileyFacial) mFacialAdapters.get(current) .getItem(position); if (smiley.getResId() == R.drawable.facial_del_selector) {// 删除按钮 int selection = mMsgEdit.getSelectionStart(); String text = mMsgEdit.getText().toString(); if (selection > 0) { String str = text.substring(selection - 1); if ("]".equals(str)) {// 光标以前的字符串中,最后一个是表情符 int start = text.lastIndexOf("["); int end = selection; mMsgEdit.getText().delete(start, end); } else// 正常字符 { mMsgEdit.getText().delete(selection - 1, selection); } } } if (!TextUtils.isEmpty(smiley.getName())) {// 选择表情 SpannableString spannableString = FacialUtils.getInstace() .addFacial(getContext(), smiley.getResId(), smiley.getName()); // 将表情显示在编辑框中 mMsgEdit.append(spannableString); } } }
FacialUtil.java:初始化数据,和添加,显示表情的工具类
public class FacialUtils { /** 每一页表情的个数,不包含最后一个删除键 */ private int pageSize = 20; private static FacialUtils mFacialUtil; /** <表情对应字符串,资源ID> 比如:<[哈哈],0x7f040000> */ private HashMap<String, Integer> smileyMap = new HashMap<String, Integer>(); /** 表情集合 */ private List<SmileyFacial> smileys = new ArrayList<SmileyFacial>(); /** 表情分页的结果集合 */ private List<List<SmileyFacial>> smileyLists = new ArrayList<List<SmileyFacial>>(); private FacialUtils() { } public static FacialUtils getInstace() { if (mFacialUtil == null) { mFacialUtil = new FacialUtils(); } return mFacialUtil; } /** * 初始化表情和对应字符串 */ public void InitFacialData(Context context) { // 得到所有表情的字符串 String[] data = context.getResources().getStringArray( R.array.smiley_values); if (data == null) { return; } SmileyFacial smileyEentry; try { for (int i = 0; i < data.length; i++) { String pngName = "smiley_" + i; // 根据图片名获得资源ID int resID = context.getResources().getIdentifier(pngName, "drawable", context.getPackageName()); smileyMap.put(data[i], resID); if (resID != 0) { smileyEentry = new SmileyFacial(); smileyEentry.setResId(resID); smileyEentry.setName(data[i]); smileys.add(smileyEentry); } } // 表情页的页数,向上取整 int pageCount = (int) Math.ceil(smileys.size() / pageSize + 0.1); for (int i = 0; i < pageCount; i++) { smileyLists.add(getPageFacials(i)); } } catch (Exception e) { e.printStackTrace(); } } /** * 获得分页后的表情集合 */ public List<List<SmileyFacial>> getFacialData() { return smileyLists; } /** * 将字符串进行正则匹配,用于显示表情 * * @param str * 含有[**]表情标识的字串 * @return 可通过TextView或EditText的setText方法直接显示表情的SpannableString */ public SpannableString showFacial(Context context, String str) { SpannableString spannableString = new SpannableString(str); // 正则表达式比配字符串里是否含有表情,如: 我好[开心]啊 String regex = "\\[[^\\]]+\\]"; // 通过传入的正则表达式来生成一个pattern Pattern sinaPatten = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); try { dealExpression(context, spannableString, sinaPatten, 0); } catch (Exception e) { Log.e("dealExpression", e.getMessage()); } return spannableString; } /** * 添加表情 */ public SpannableString addFacial(Context context, int imgId, String str) { if (TextUtils.isEmpty(str)) { return null; } Drawable drawable = context.getResources().getDrawable(imgId); drawable.setBounds(0, 0, 30, 30); ImageSpan imageSpan = new ImageSpan(drawable); SpannableString spannable = new SpannableString(str); spannable.setSpan(imageSpan, 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return spannable; } /** * 对spanableString进行正则判断,如果符合要求,则以表情图片代替 */ private void dealExpression(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; } int resId = smileyMap.get(key); if (resId != 0) { Drawable drawable = context.getResources().getDrawable(resId); drawable.setBounds(0, 0, 30, 30); // 通过图片资源id来得到drawable,用一个ImageSpan来包装 ImageSpan imageSpan = new ImageSpan(drawable); // 计算该图片名字的长度,也就是要替换的字符串的长度 int end = matcher.start() + key.length(); // 将该图片替换字符串中规定的位置中 spannableString.setSpan(imageSpan, matcher.start(), end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); if (end < spannableString.length()) { // 如果整个字符串还未验证完,则继续。。 dealExpression(context, spannableString, patten, end); } break; } } } /** * 获取分页数据 */ private List<SmileyFacial> getPageFacials(int page) { int startIndex = page * pageSize; int endIndex = startIndex + pageSize; if (endIndex > smileys.size()) { endIndex = smileys.size(); } List<SmileyFacial> list = new ArrayList<SmileyFacial>(); list.addAll(smileys.subList(startIndex, endIndex)); if (list.size() < pageSize) { // 填充空白区域 for (int i = list.size(); i < pageSize; i++) { SmileyFacial object = new SmileyFacial(); list.add(object); } } if (list.size() == pageSize) { // 最后一个为删除键 SmileyFacial object = new SmileyFacial(); object.setResId(R.drawable.facial_del_selector); list.add(object); } return list; } }
facial_edit_layout.xml:自定义View的布局文件
<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/facialEditLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <RelativeLayout android:id="@+id/facial_editLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/chatting_bottom_bg" android:padding="5dp" > <ImageView android:id="@+id/facial_enable" android:layout_width="35dp" android:layout_height="35dp" android:layout_alignBottom="@+id/facial_edit" android:layout_alignParentLeft="true" android:padding="5dp" android:src="@drawable/facial_btn_normal" /> <EditText android:id="@+id/facial_edit" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_toLeftOf="@+id/facial_sendBtn" android:layout_toRightOf="@+id/facial_enable" android:background="@drawable/facial_edit_selector" android:inputType="textMultiLine" android:maxLines="3" android:textSize="16sp" /> <Button android:id="@+id/facial_sendBtn" android:layout_width="50dp" android:layout_height="35dp" android:layout_alignBottom="@+id/facial_edit" android:layout_alignParentRight="true" android:background="@drawable/facial_send_btn_selector" android:text="@string/btn_send" /> </RelativeLayout> <!-- 表情区域 --> <RelativeLayout android:id="@+id/facial_facialLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/facial_editLayout" android:visibility="gone" android:windowSoftInputMode="adjustPan" > <android.support.v4.view.ViewPager android:id="@+id/facial_viewPager" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_above="@+id/facial_cursor" android:layout_marginTop="10dp" > </android.support.v4.view.ViewPager> <LinearLayout android:id="@+id/facial_cursor" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:layout_marginTop="5dp" android:gravity="center" android:orientation="horizontal" /> </RelativeLayout> </LinearLayout>
3. 表情输入框的使用
在聊天界面中,需要使用表情输入框。使用比较简单,只要实现发送按钮的点击函数(setOnSendBtnClickListener)即可。同时,可以使用getMsgEditTxt获得消息内容,clearMsgEdit清空文本,isFacialShow判断表情框是否显示,以及hideFacialView隐藏表情框。
ChattingActivity.java:聊天界面Activity。主要贴出使用表情输入框的代码。
public class ChattingActivity extends ActionBarActivity{ private ListView mListView; private FacialEditLayout mFacialEditLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_chatting); findViews(); setListener(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == KeyEvent.KEYCODE_BACK) { if(mFacialEditLayout.isFacialShow()) { //隐藏表情框 mFacialEditLayout.hideFacialView(); return true; } } return super.onKeyDown(keyCode, event); } private void setListener() { //点击聊天消息的界面,隐藏键盘或表情框 mListView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { hideKeyBoard(); mFacialEditLayout.hideFacialView(); return false; } }); //设置发送按钮点击监听 mFacialEditLayout.setOnSendBtnClickListener(new FacialEditLayout.OnSendBtnClickListener() { @Override public void onBtnClicked(String input) { //清空编辑框 mFacialEditLayout.clearMsgEdit(); //发送消息 } }); } private void findViews() { mListView = (ListView) findViewById(R.id.chatting_listView); mFacialEditLayout = (FacialEditLayout) findViewById(R.id.chatting_facialLayout); } /** 隐藏软键盘 */ private void hideKeyBoard() { ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)) .hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } }
参考网址:http://blog.csdn.net/lnb333666/article/details/8546497
首页Android聊天软件的开发
相关文章推荐
- Android聊天软件的开发(二)--数据库
- Android聊天软件的开发(五)--头像设置
- Android聊天软件的开发
- Android聊天软件界面开发
- Android聊天软件的开发
- Android聊天软件的开发(七)--聊天通信
- android聊天软件开发,实战篇(1)
- android聊天软件开发,实战篇(2)
- android聊天表情开发
- Android聊天软件的开发(三)--网络连接
- 适用于Android开发的简单聊天软件
- Android聊天软件的开发--聊天通信
- Android聊天软件的开发(四)--通讯录
- Android聊天软件的开发(一)--预备知识
- 高通Android手机软件开发培训
- Android软件开发实例:用客户端写博客
- Android开发必备浏览器软件
- android 软件开发趋势
- android 条码识别软件开发全解析(续2详解绝杀!)