当前位置: 首页>編程日記>正文

一步一步教你实现Emoji表情键盘

一步一步教你实现Emoji表情键盘

一、 说明


说到聊天,就离不开文字、表情和图片,表情和图片增加了聊天的趣味性,让原本无聊的文字瞬间用表情动了起来,今天给大家带来的是表情键盘,教你一步一步实现,先来看下效果图.
这里写图片描述

二、功能


1、如何控制表情键盘与输入法的切换
2、如何解析表情
3、如何处理表情与非表情的删除

三、实现


明确了各个要解决的问题,下面我们逐个来实现

表情键盘与输入法切换
博主查了一下相关资料,有如下方案

方案一:动态改变SoftInputMode
软键盘显示时将SoftInputMode设置为「stateVisible|adjustResize」,表情键盘显示时调整为「adjustPan」

方案二:Dialog
直接在软键盘上显示一个Dialog,可避开大部分切换逻辑,但是在打开当前页面后存在软键盘和Dialog冲突问题

博主在观察QQ、微信、微博、陌陌后发现,他们的表情键盘和软键盘切换,并不会导致聊天内容(ListView、RecyclerView)的跳动,基本就可以推测SoftInputMode就是adjustsPan(SoftInputMode含义)
明确了adjustPan那就好办了,既然聊天内容(ListView、RecyclerView)不会跳动,那么在软键盘切换至表情键盘的时候,底部肯定有一个和软键盘高度一致的View,只需在点击表情的时候将软键盘隐藏,显示表情键盘,在点击EditText的时候显示软键盘,隐藏表情键盘。

来梳理一下知识点:

1、如何获取软键盘高度
2、如何手动控制软键盘的显示与隐藏
3、如何避免在别的页面切到当前界面因软键盘的状态变化而冲突

获取软键盘高度

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自身的高度,就得到了软键盘的高度,但是发现在有虚拟按键的手机上在没有显示软键盘时减出来的高度总是144,后来查了下资料,发现在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() {@Overridepublic void run() {mInputManager.showSoftInput(mEditText, 0);}});}private void hideSoftInput() {mInputManager.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);}

博主在测试后发现一个问题,点击表情按钮,输入框会抖动,分析下这个过程,点击表情按钮,关闭软键盘,此时Activity的高度发生变化,高度变高,输入框回到底部,再打开表情键盘,此时输入框又被顶上来,输入框看起来上下抖动,经多次测试发现无论是先隐藏软键盘还是先显示表情键盘都存在这个问题,思考过后,既然输入框会上下抖动,那么固定它的位置不就行了,那么问题来了,如何固定它的位置呢?举个栗子,假如在一个LinearLayout里面有若干个控件,如果里面的控件的位置大小都不变,那么即使在软键盘显示和隐藏(Activity的高度发生变化),也不会隐藏输入框的位置,自然也就不会发生跳动问题。

锁定解锁内容高度(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() {@Overridepublic void run() {((LinearLayout.LayoutParams) mContentView.getLayoutParams()).weight = 1.0F;}}, 200L);}

表情面板控制

 public EmotionInputDetector bindToEmotionButton(final CheckBox emotionButton) {mEmojiView = emotionButton;emotionButton.setOnClickListener(new View.OnClickListener() {@Overridepublic 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、如何将表情码和表情建立联系
2、如何给表情分页
3、如何将表情码转换成表情

将表情码和表情以键值对的形式建立联系

ArrayMap<String, Integer> emoJiMap = new ArrayMap<String,Integer>();

key(表情码)value(表情地址)

        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<>();//每一页表情的viewmPageNum = (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());elseemojiPageList = 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() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int positionIndex, long id) {String fileName = mEmoJiAdapter.getItem(positionIndex);if (fileName != "[删除]") { // 不是删除键,显示表情showEmoJi(fileName);} else { // 删除文字或者表情deleteContent();}}});return containerView;}

将表情面板的表情码转解析成表情

  @Overridepublic 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名获取对应的表情idif (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);}}}}

四、成果


这里写图片描述

github:
EasyEmoji


https://www.fengoutiyan.com/post/14914.html

相关文章:

  • 鏡像模式如何設置在哪,圖片鏡像操作
  • 什么軟件可以把圖片鏡像翻轉,C#圖片處理 解決左右鏡像相反(旋轉圖片)
  • 手機照片鏡像翻轉,C#圖像鏡像
  • 視頻鏡像翻轉軟件,python圖片鏡像翻轉_python中鏡像實現方法
  • 什么軟件可以把圖片鏡像翻轉,利用PS實現圖片的鏡像處理
  • 照片鏡像翻轉app,java實現圖片鏡像翻轉
  • 什么軟件可以把圖片鏡像翻轉,python圖片鏡像翻轉_python圖像處理之鏡像實現方法
  • matlab下載,matlab如何鏡像處理圖片,matlab實現圖像鏡像
  • 圖片鏡像翻轉,MATLAB:鏡像圖片
  • 鏡像翻轉圖片的軟件,圖像處理:實現圖片鏡像(基于python)
  • canvas可畫,JavaScript - canvas - 鏡像圖片
  • 圖片鏡像翻轉,UGUI優化:使用鏡像圖片
  • Codeforces,CodeForces 1253C
  • MySQL下載安裝,Mysql ERROR: 1253 解決方法
  • 勝利大逃亡英雄逃亡方案,HDU - 1253 勝利大逃亡 BFS
  • 大一c語言期末考試試題及答案匯總,電大計算機C語言1253,1253《C語言程序設計》電大期末精彩試題及其問題詳解
  • lu求解線性方程組,P1253 [yLOI2018] 扶蘇的問題 (線段樹)
  • c語言程序設計基礎題庫,1253號C語言程序設計試題,2016年1月試卷號1253C語言程序設計A.pdf
  • 信奧賽一本通官網,【信奧賽一本通】1253:抓住那頭牛(詳細代碼)
  • c語言程序設計1253,1253c語言程序設計a(2010年1月)
  • 勝利大逃亡英雄逃亡方案,BFS——1253 勝利大逃亡
  • 直流電壓測量模塊,IM1253B交直流電能計量模塊(艾銳達光電)
  • c語言程序設計第三版課后答案,【渝粵題庫】國家開放大學2021春1253C語言程序設計答案
  • 18轉換為二進制,1253. 將數字轉換為16進制
  • light-emitting diode,LightOJ-1253 Misere Nim
  • masterroyale魔改版,1253 Dungeon Master
  • codeformer官網中文版,codeforces.1253 B
  • c語言程序設計考研真題及答案,2020C語言程序設計1253,1253計算機科學與技術專業C語言程序設計A科目2020年09月國家開 放大學(中央廣播電視大學)
  • c語言程序設計基礎題庫,1253本科2016c語言程序設計試題,1253電大《C語言程序設計A》試題和答案200901
  • 肇事逃逸車輛無法聯系到車主怎么辦,1253尋找肇事司機