思路:
表情图片资源
表情展示面板
输入删除逻辑
首先需要下载表情相关资源,链接:http://pan.baidu.com/s/1pLLTEkV 密码:etow,下载完后将图片添加到项目中。
然后自定义一个工具类,作用一:使得表情图片的文件名和在项目中的资源id作为map集合的键值对一一对应。
public static final int[] EmojiResArray = {
R.drawable.d_aini,
R.drawable.d_aoteman,
R.drawable.d_baibai,
...
R.drawable.w_yueliang,
};
public static final String[] EmojiTextArray = {
"[爱你]",
"[奥特曼]",
"[拜拜]",
...
"[月亮]",
};
private static final Map<String, Integer> EmojiMap = new HashMap<>();
static {
for (int i = 0; i < EmojiResArray.length; i++) {
EmojiMap.put(EmojiTextArray[i], EmojiResArray[i]);
}
}
public static int getImgByName(String key) {
return EmojiMap.get(key);
}
作用二:用于将服务器返回的类似[爱你][奥特曼]格式的字符串转换成对应表情图片的复合文本。
//正则表达式匹配,[爱你][奥特曼]——> 表情的复合文本
public static SpannableString getEmotionContent(final Context context, final TextView tv, String source) {
SpannableString spannableString = new SpannableString(source);
Resources res = context.getResources();
String regexEmotion = "\\[([\u4e00-\u9fa5\\w])+\\]";
Pattern patternEmotion = Pattern.compile(regexEmotion);
Matcher matcherEmotion = patternEmotion.matcher(spannableString);
while (matcherEmotion.find()) {
// 获取匹配到的具体字符
String key = matcherEmotion.group();
// 匹配字符串的开始位置
int start = matcherEmotion.start();
// 利用表情名字获取到对应的图片
Integer imgRes = EmojiUtils.getImgByName(key);
if (imgRes != null) {
// 压缩表情图片
int size = (int) tv.getTextSize();
Bitmap bitmap = BitmapFactory.decodeResource(res, imgRes);
Bitmap scaleBitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);
ImageSpan span = new ImageSpan(context, scaleBitmap);
spannableString.setSpan(span, start, start + key.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return spannableString;
}
表情面板布局:一个Fragment上填充一个ViewPager容器,ViewPager每一页都是一个3*7的GridView布局,其中每一个都是一张表情图片,并为其添加点击事件。
EViewPagerAdapter和EGridViewAdapter的具体实现代码不再赘述,需要注意的是他们的数据源分别是以GridView为泛型的和Integer为泛型的list集合。
EmojiFragment中需要自定义一个内部类接口,通过接口回调返回点击的表情的顺序序号,从而获取对应的文字名
private void init() {
List<GridView> gridViewList = new ArrayList<>();
EmojiGridViewAdapter gridViewAdapter = null;
for (int i = 0; i < EmojiUtils.EmojiResArray.length; i++) {
if (i % 20 == 0) {
final int po = i;
GridView view = new GridView(getContext());
view.setNumColumns(7);
view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (po >= 20) {
mOnClickOnFragment.onClickFragment(position + po);
} else {
mOnClickOnFragment.onClickFragment(position);
}
}
});
gridViewAdapter = new EmojiGridViewAdapter();
gridViewAdapter.addInt(getContext(), EmojiUtils.EmojiResArray[i]);
view.setAdapter(gridViewAdapter);
gridViewList.add(view);
continue;
}
gridViewAdapter.addInt(getContext(), EmojiUtils.EmojiResArray[i]);
}
EmojiViewPagerAdapter adapter = new EmojiViewPagerAdapter();
adapter.setGridViews(gridViewList);
mEmojiViewPager.setAdapter(adapter);
}
public interface OnClickOnFragment {
void onClickFragment(int i);
}
使用时只需将该EmojiFragment表情面板填充到布局中即可,
//填充表情到布局中
EmojiFragment emojiFragment = new EmojiFragment();
getSupportFragmentManager().beginTransaction().add(R.id.emojiLayout, emojiFragment).commit();
输入删除逻辑思路:当我们点击表情面板来输入表情时,根据接口回调的int值得到相应的表情图片id,然后以复合文本的形式在EditText中显示,同时会对表情图片进行压缩使其大小和输入的文字一般大,
//根据数组中position获得相对应的表情的复合文本
public static SpannableString getEmotionEditText(Context context, EditText editText, int position) {
SpannableString spanString = new SpannableString(" ");
int size = (int) editText.getTextSize();
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), EmojiUtils.EmojiResArray[position]);
Bitmap scaleBitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);
ImageSpan span = new ImageSpan(context, scaleBitmap);
spanString.setSpan(span, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spanString;
}
但是不会将该复合文本提交服务器,而是将表情图片对应的文件名以"[爱你]"的格式拼接到mCommitString
字符串中,然后才提交服务器。
需要注意的是:一个表情图片的复合文本添加到EditText中只占一个字符,但是需要拼接的[爱你]确实四个字符串,这样是如何在EditText中任意输入表情时,mCommitString
字符串是如何在准确位置拼接的?
定义一个泛型是Integer的list集合,该集合的索引值看做是mEdiText中的字符的排列位置,同时也是mCommitString
字符串中字符的排列位置,该集合的value值则是该位置在mCommitString
字符串中的长度,比如:"我[爱你]"在EditText中第2个位置是一个表情的复合文本,只占1个字符,但是在mCommitString
字符串中第2个位置确实4个字符,此时该集合索引为1时,value值为1,索引为2时,value值为4;
//表情的点击监听事件
emojiFragment.setOnClickOnFragment(new EmojiFragment.OnClickOnFragment() {
@Override
public void onClickFragment(int i) {
//鼠标光标选中的位置
int selectionStart = mEditText.getSelectionStart();
//EditText中添加复合文本
SpannableString emotionEditText = EmojiUtils.getEmotionEditText(SendCardDetailsActivity.this, mEditText, i);
mEditText.getText().insert(selectionStart, emotionEditText);
mList.add(selectionStart, EmojiUtils.EmojiTextArray[i].length());
int x = 0;
for (int i1 = 0; i1 < selectionStart; i1++) {
if (mList.get(i1) != null) {
x += mList.get(i1);
}
}
LogUtils.d(TAG, "x:" + x);
mCommitString.insert(x, EmojiUtils.EmojiTextArray[i].trim());
}
当在EditText的任意位置插入的不是表情而是空格,汉字,字母时也面临这一个问题,就是在EditText的某一位置插入一个字符时,同时需要在mCommitString
字符串准确插入该字符,由于在某一位置是一对多的关系,就很有可能造成提交的字符串位置错乱,比如向"我[爱你]的后面插入"呀"字,就有可能出现"我[呀爱你]"这种现象;
解决办法就是:在EditText中插入字符时,得到插入的初始位置,然后遍历累加小于该初始值所有索引的value,得到的就是mCommitString
字符串中将要插入的准确初始位置,删除也是同样原理,初始位置和结束位置之间索引对应的value值累加就是将要删除的真正长度,
//内容EditText 文本改变监听
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
LogUtils.d(TAG, start + ":" + before + ";" + count + "");
int y1 = 0;
int y2 = 0;
int x1 = 0;
int x2 = start;
//count ==0 时是删除操作
if (count == 0) {
for (int i = 0; i <= start; i++) {
if (mList.get(i) != null) {
y1 += mList.get(i);
}
}
for (int i = 0; i <= start - before; i++) {
if (mList.get(i) != null) {
y2 += mList.get(i);
}
}
LogUtils.d(TAG, "y1:" + y1);
LogUtils.d(TAG, "y2:" + y2);
mCommitString.delete(y2, y1);
} else {
//如果是一次输入一大段汉字,此时就不是一对多的关系,EditText和`mCommitString`字符串就是一对一的关系,在集合中插入大段字符串的长度的索引区间的value值都赋值为1,就保证集合长度和字符串长度一致
for (int i = 0; i < count; i++) {
mList.add(x2++, 1);
}
for (int i = 0; i < start; i++) {
if (mList.get(i) != null) {
x1 += mList.get(i);
}
}
LogUtils.d(TAG, "x1:" + x1);
//当是输入表情时,忽略空格;否则允许输入空格
if (mIsEmoji) {
mCommitString.insert(x1, s.toString().substring(start, start + count).trim());
} else
mCommitString.insert(x1, s.toString().substring(start, start + count));
mIsEmoji = false;
}
LogUtils.d(TAG, mCommitString.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
笔者水平有限,希望大家教我
网友评论