表情用于聊天对话的输入,实现的原理主要是:在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:带表情输入的自定义编辑框布局。
public class FacialEditLayout extends LinearLayout implements
OnItemClickListener, OnClickListener {
private Context mContext;
/** 发送按钮的监听事件 */
private OnSendBtnClickListener mSendBtnListener;
/** 显示表情页的viewpager */
private ViewPager mFacialViewPager;
/** 表情页界面集合 */
private ArrayList mPageViews;
/** 游标显示布局 */
private LinearLayout mCursorLayout;
/** 游标点集合 */
private ArrayList mCursorViews;
/** 表情集合 页+个 */
private List> smileys;
/** 表情区域 */
private View mFacialLayout;
/** 打开或关闭表情的按钮 */
private ImageView mFacialEnable;
/** 输入框 */
private EditText mMsgEdit;
/** 发送按钮 */
private Button mSendBtn;
/** 表情数据填充器 */
private List 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("");
}
/**
* 判断表情框是否显示
* 一般用于重写返回按键时调用
*/
public boolean isFacialShow() {
return (mFacialLayout.getVisibility() == View.VISIBLE);
}
/**
* 隐藏表情选择框
* 一般用于重写返回按键时调用
*/
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 nullView1 = new View(mContext);
// 设置透明背景
nullView1.setBackgroundColor(Color.TRANSPARENT);
mPageViews.add(nullView1);
// 中间添加表情页
mFacialAdapters = new ArrayList();
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;
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);
}
}
}
public class FacialUtils {
/** 每一页表情的个数,不包含最后一个删除键 */
private int pageSize = 20;
private static FacialUtils mFacialUtil;
/** <表情对应字符串,资源ID> 比如:<[哈哈],0x7f040000> */
private HashMap smileyMap = new HashMap();
/** 表情集合 */
private List smileys = new ArrayList();
/** 表情分页的结果集合 */
private List> smileyLists = new ArrayList>();
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> 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 getPageFacials(int page) {
int startIndex = page * pageSize;
int endIndex = startIndex + pageSize;
if (endIndex > smileys.size()) {
endIndex = smileys.size();
}
List list = new ArrayList();
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;
}
}
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聊天软件的开发