背景:
說到聊天,就離不開文字、表情和圖片,表情和圖片增加了聊天的趣味性,讓原本無聊的文字瞬間用表情動了起來,今天給大家?guī)淼氖潜砬殒I盤,教你一步一步實現(xiàn),先來看下效果圖:
效果圖

功能:
1、如何控制表情鍵盤與輸入法的切換
2、如何解析表情
3、如何處理表情與非表情的刪除
實現(xiàn):
明確了各個要解決的問題,下面我們逐個來實現(xiàn)
表情鍵盤與輸入法切換
查了一下相關資料,有如下方案:
方案一:動態(tài)改變SoftInputMode
軟鍵盤顯示時將SoftInputMode設置為「stateVisible|adjustResize」,表情鍵盤顯示時調整為「adjustPan」
方案二:Dialog
直接在軟鍵盤上顯示一個Dialog,可避開大部分切換邏輯,但是在打開當前頁面后存在軟鍵盤和Dialog沖突問題
觀察QQ、微信、微博、陌陌后發(fā)現(xiàn),他們的表情鍵盤和軟鍵盤切換,并不會導致聊天內容(ListView、RecyclerView)的跳動,基本就可以推測SoftInputMode就是adjustsPan。
明確了adjustPan那就好辦了,既然聊天內容(ListView、RecyclerView)不會跳動,那么在軟鍵盤切換至表情鍵盤的時候,底部肯定有一個和軟鍵盤高度一致的View,只需在點擊表情的時候將軟鍵盤隱藏,顯示表情鍵盤,在點擊EditText的時候顯示軟鍵盤,隱藏表情鍵盤。
來梳理一下知識點:
1、如何獲取軟鍵盤高度
2、如何手動控制軟鍵盤的顯示與隱藏
3、如何避免在別的頁面切到當前界面因軟鍵盤的狀態(tài)變化而沖突
獲取軟鍵盤高度
private int getSupportSoftInputHeight() {
Rect r = new Rect();
mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
int screenHeight = mActivity.getWindow().getDecorView().getRootView().getHeight();
int softInputHeight = screenHeight - r.bottom;
if (Build.VERSION.SDK_INT >= 20) {
// When SDK Level >= 20 (Android L),
// the softInputHeight will contain the height of softButtonsBar (if has)
softInputHeight = softInputHeight - getSoftButtonsBarHeight();
}
if (softInputHeight < 0) {
Log.w("EmotionInputDetector", "Warning: value of softInputHeight is below zero!");
}
if (softInputHeight > 0) {
sp.edit().putInt(SHARE_PREFERENCE_TAG, softInputHeight).apply();
}
return softInputHeight;
}
這里的原理是通過當前Activity獲取RootView的高度減去Activity自身的高度,就得到了軟鍵盤的高度,但是發(fā)現(xiàn)在有虛擬按鍵的手機上在沒有顯示軟鍵盤時減出來的高度總是144,后來查了下資料,發(fā)現(xiàn)在API>18時有軟鍵盤的手機需要減去底部虛擬按鍵的高度。
? ? @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private int getSoftButtonsBarHeight() {
DisplayMetrics metrics = new DisplayMetrics();
mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int usableHeight = metrics.heightPixels;
mActivity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
int realHeight = metrics.heightPixels;
if (realHeight > usableHeight) {
return realHeight - usableHeight;
} else {
return 0;
}
}
把獲取到的高度設置給表情鍵盤
? ? private void showEmotionLayout() {
int softInputHeight = getSupportSoftInputHeight();
if (softInputHeight == 0) {
softInputHeight = sp.getInt(SHARE_PREFERENCE_TAG, 400);
}
hideSoftInput();
mEmotionLayout.getLayoutParams().height = softInputHeight;
mEmotionLayout.setVisibility(View.VISIBLE);
}
控制表情的顯示與隱藏
? ? private void showSoftInput() {
mEditText.requestFocus();
mEditText.post(new Runnable() {
@Override
public void run() {
mInputManager.showSoftInput(mEditText, 0);
}
});
}
private void hideSoftInput() {
mInputManager.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
}
在測試后發(fā)現(xiàn)一個問題,點擊表情按鈕,輸入框會抖動,分析下這個過程,點擊表情按鈕,關閉軟鍵盤,此時Activity的高度發(fā)生變化,高度變高,輸入框回到底部,再打開表情鍵盤,此時輸入框又被頂上來,輸入框看起來上下抖動,經多次測試發(fā)現(xiàn)無論是先隱藏軟鍵盤還是先顯示表情鍵盤都存在這個問題,思考過后,既然輸入框會上下抖動,那么固定它的位置不就行了,那么問題來了,如何固定它的位置呢?
舉個栗子,假如在一個LinearLayout里面有若干個控件,如果里面的控件的位置大小都不變,那么即使在軟鍵盤顯示和隱藏(Activity的高度發(fā)生變化),也不會隱藏輸入框的位置,自然也就不會發(fā)生跳動問題。
鎖定解鎖內容高度(ListView、RecyclerView)
? ? private void lockContentHeight() {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mContentView.getLayoutParams();
params.height = mContentView.getHeight();
params.weight = 0.0F;
}
private void unlockContentHeightDelayed() {
mEditText.postDelayed(new Runnable() {
@Override
public void run() {
((LinearLayout.LayoutParams) mContentView.getLayoutParams()).weight = 1.0F;
}
}, 200L);
}
表情面板控制
? ? public EmotionInputDetector bindToEmotionButton(final CheckBox emotionButton) {
mEmojiView = emotionButton;
emotionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mEmotionLayout.isShown()) {
lockContentHeight();
hideEmotionLayout(true);
mEmojiView.setChecked(false);
unlockContentHeightDelayed();
} else {
if (isSoftInputShown()) {
lockContentHeight();
showEmotionLayout();
mEmojiView.setChecked(true);
unlockContentHeightDelayed();
} else {
showEmotionLayout();
}
}
}
});
return this;
}
表情解析
問題分析:
1、如何將表情碼和表情建立聯(lián)系
2、如何給表情分頁
3、如何將表情碼轉換成表情文章來源:http://www.zghlxwxcb.cn/news/detail-599407.html
將表情碼和表情以鍵值對的形式建立聯(lián)系
ArrayMap<String, Integer> emoJiMap = new ArrayMap<String,Integer>();
key(表情碼)value(表情地址)文章來源地址http://www.zghlxwxcb.cn/news/detail-599407.html
? ? ? ? emoJiMap.put("[emoji_1]",R.drawable.emoji_1);
emoJiMap.put("[emoji_2]",R.drawable.emoji_2);
emoJiMap.put("[emoji_3]",R.drawable.emoji_3);
emoJiMap.put("[emoji_4]",R.drawable.emoji_4);
emoJiMap.put("[emoji_5]",R.drawable.emoji_5);
emoJiMap.put("[emoji_6]",R.drawable.emoji_6);
emoJiMap.put("[emoji_7]",R.drawable.emoji_7);
emoJiMap.put("[emoji_8]",R.drawable.emoji_8);
emoJiMap.put("[emoji_9]",R.drawable.emoji_9);
emoJiMap.put("[emoji_10]",R.drawable.emoji_10);
emoJiMap.put("[emoji_11]",R.drawable.emoji_11);
emoJiMap.put("[emoji_12]",R.drawable.emoji_12);
emoJiMap.put("[emoji_13]",R.drawable.emoji_13);
emoJiMap.put("[emoji_14]",R.drawable.emoji_14);
emoJiMap.put("[emoji_15]",R.drawable.emoji_15);
emoJiMap.put("[emoji_16]",R.drawable.emoji_16);
emoJiMap.put("[emoji_17]",R.drawable.emoji_17);
emoJiMap.put("[emoji_18]",R.drawable.emoji_18);
emoJiMap.put("[emoji_19]",R.drawable.emoji_19);
emoJiMap.put("[emoji_20]",R.drawable.emoji_20);
將表情面板的表情碼用List進行保存
List<String> emojiList = new ArrayList<String>();
? ? ? ? emojiList.add("[emoji_1]");
emojiList.add("[emoji_2]");
emojiList.add("[emoji_3]");
emojiList.add("[emoji_4]");
emojiList.add("[emoji_5]");
emojiList.add("[emoji_6]");
emojiList.add("[emoji_7]");
emojiList.add("[emoji_8]");
emojiList.add("[emoji_9]");
emojiList.add("[emoji_10]");
emojiList.add("[emoji_11]");
emojiList.add("[emoji_12]");
emojiList.add("[emoji_13]");
emojiList.add("[emoji_14]");
emojiList.add("[emoji_15]");
emojiList.add("[emoji_16]");
emojiList.add("[emoji_17]");
emojiList.add("[emoji_18]");
emojiList.add("[emoji_19]");
emojiList.add("[emoji_20]");
計算表情頁
? ? public List<View> getPagers() {
List<View> pageViewList = new ArrayList<>();
//每一頁表情的view
mPageNum = (int) Math.ceil(mEmoJiResList.size() * 1.0f / EMOJI_PAGE_COUNT);
for (int position = 1; position <= mPageNum; position++) {
pageViewList.add(getGridView(position));
}
return pageViewList;
}
表情分頁
? ? public View getGridView(int position) {
List mEmoJiList = new ArrayList<>();
View containerView = View.inflate(mContext, R.layout.container_gridview, null);
ExpandGridView eg_gridView = (ExpandGridView) containerView.findViewById(R.id.eg_gridView);
eg_gridView.setGravity(Gravity.CENTER_VERTICAL);
List<String> emojiPageList = null;
if (position == mPageNum)//最后一頁
emojiPageList = mEmoJiResList.subList((position - 1) * EMOJI_PAGE_COUNT, mEmoJiResList.size());
else
emojiPageList = mEmoJiResList.subList((position - 1) * EMOJI_PAGE_COUNT, EMOJI_PAGE_COUNT * position);
mEmoJiList.addAll(emojiPageList);
//添加刪除表情
mEmoJiList.add("[刪除]");
final EmoJiAdapter mEmoJiAdapter = new EmoJiAdapter(mContext, position, mEmoJiList);
eg_gridView.setAdapter(mEmoJiAdapter);
eg_gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int positionIndex, long id) {
String fileName = mEmoJiAdapter.getItem(positionIndex);
if (fileName != "[刪除]") { // 不是刪除鍵,顯示表情
showEmoJi(fileName);
} else { // 刪除文字或者表情
deleteContent();
}
}
});
return containerView;
}
將表情面板的表情碼轉解析成表情
? ? @Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(getContext(), R.layout.item_row_emoji, null);
}
ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_emoji);
String fileName = getItem(position);
Integer resId = EmoJiUtils.getEmoJiMap().get(fileName);
if (resId != null) {
Drawable drawable = getContext().getResources().getDrawable(resId);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
imageView.setImageResource(resId);
}
return convertView;
}
輸入框表情碼轉換成表情
? ? public static SpannableString parseEmoJi(Context context, String content) {
SpannableString spannable = new SpannableString(content);
String reg = "\\[[a-zA-Z0-9_\\u4e00-\\u9fa5]+\\]";//校驗表情正則
Pattern pattern = Pattern.compile(reg);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
String regEmoJi = matcher.group();//獲取匹配到的emoji字符串
int start = matcher.start();//匹配到字符串的開始位置
int end = matcher.end();//匹配到字符串的結束位置
Integer resId = emoJiMap.get(regEmoJi);//通過emoji名獲取對應的表情id
if (resId != null) {
Drawable drawable = context.getResources().getDrawable(resId);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(drawable, content);
spannable.setSpan(imageSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return spannable;
}
? ? private void showEmoJi(String fileName) {
int selectionStart = mInputContainer.getSelectionStart();
String body = mInputContainer.getText().toString();
StringBuilder stringBuilder = new StringBuilder(body);
stringBuilder.insert(selectionStart, fileName);
mInputContainer.setText(EmoJiUtils.parseEmoJi(mContext, stringBuilder.toString()));
mInputContainer.setSelection(selectionStart + fileName.length());
}
表情刪除
? ? private void deleteContent() {
if (!TextUtils.isEmpty(mInputContainer.getText())) {
int selectionStart = mInputContainer.getSelectionStart();//獲取光標位置
if (selectionStart > 0) {
String body = mInputContainer.getText().toString();
String lastStr = body.substring(selectionStart - 1, selectionStart);//獲取最后一個字符
if (lastStr.equals("]")) {//表情
if (selectionStart < body.length()) {//從中間開始刪除
body = body.substring(0, selectionStart);
}
int i = body.lastIndexOf("[");
if (i != -1) {
String tempStr = body.substring(i, selectionStart);//截取表情碼
if (EmoJiUtils.getEmoJiMap().containsKey(tempStr)) {//校驗是否是表情
mInputContainer.getEditableText().delete(i, selectionStart);//刪除表情
} else {
mInputContainer.getEditableText().delete(selectionStart - 1, selectionStart);//刪除一個字符
}
} else {
mInputContainer.getEditableText().delete(selectionStart - 1, selectionStart);
}
} else {//非表情
mInputContainer.getEditableText().delete(selectionStart - 1, selectionStart);
}
}
}
}
源碼地址:
https://github.com/diycoder/EasyEmoji
到了這里,關于Android一步一步教你實現(xiàn)Emoji表情鍵盤的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!