在做一些社会化APP时,用户总是青睐使用表情,下面就探究一下如何在APP中添加表情。在支持输入表情时,一般要涉及到表情框&&键盘的切换,需要有一个按钮,来触发事件!这里仅仅是一个雏形,存在一些问题,在这里暴露一下:
1.表情&&键盘切换时,会发生跳动;
2.这里的输入框,仅仅支持一个。

1.键盘弹出时,将整个页面向上移动

在AndroidManifest,activity标签中添加windowSoftInputMode=adjustResize,键盘弹出时,整个页面就会向上移动,这样咱们放置在页面底部的键盘&&表情切换的按钮,就一直可见了。

 <activity
            android:windowSoftInputMode="adjustResize"android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>

给表情框留下位置

 <com.util.emotions.IMEBar
            android:id="@+id/ime_bar"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@id/btn"android:orientation="vertical"><!-- 输入输 --><EditText
                android:id="@+id/et_input"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:gravity="left|top"android:hint="分享新鲜事..."android:inputType="textMultiLine"android:minHeight="200dp" /><LinearLayout
                android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#eeeeee"android:gravity="center_vertical"android:orientation="horizontal"android:padding="8dp"><ImageView
                    android:id="@+id/iv_switch"android:layout_width="wrap_content"android:layout_height="wrap_content"android:contentDescription="null"android:src="@drawable/icon_icon" /></LinearLayout><!-- 表情/keyboard--><FrameLayout
                android:id="@+id/fl_content"android:layout_width="match_parent"android:layout_height="wrap_content" /></com.util.emotions.IMEBar>

R.id.fl_content就是为表情框留下的位置。当键盘不可见的时候,R.id.fl_content就填充表情;当键盘可见时,R.id.fl_content GONE.
他俩不协调就出现了跳动的问题。

键盘&&表情面板切换的关键IMEBar

package com.util.emotions;import android.content.Context;
import android.text.Editable;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;import com.hang.emojidemo.R;public class IMEBar extends LinearLayout implements OnClickListener,OnEmojiClickListener {public IMEBar(Context context) {super(context);init();}public IMEBar(Context context, AttributeSet attrs) {super(context, attrs);init();}public IMEBar(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}public void init() {/*在构造方法中,IMEBar实例化尚未完成,下面的操作,必须延迟执行;new Thread? Noonly the original thread that created a view hierarchy can touch its views这个和只有UI线程才能修改UI,是一样的通过下面的方法还是比较好的*/this.postDelayed(new Runnable() {@Overridepublic void run() {getEditText(IMEBar.this);if (mEditText == null) {throw new RuntimeException("must contain a EditText  for putting content in");}mEditText.setOnClickListener(IMEBar.this);setup();}}, 100);}
/*通过递归的方法,获取EditText
*/public void getEditText(ViewGroup viewGroup) {for (int j = 0; j < viewGroup.getChildCount(); j++) {View childView = viewGroup.getChildAt(j);if (childView instanceof ViewGroup) {getEditText((ViewGroup) childView);} else {if (childView instanceof EditText) {mEditText = (EditText) childView;}}}}private InputMethodManager mInputMethodManager;private EmojiUtil mEmojiUtil;private EditText mEditText;/*** 放置弹出内容的区域,需要一个id为fl_content的FrameLayout*/private FrameLayout mFrameContent;/*** 表情栏*/private EmojiPanel mEmotionsPanel;private ImageView mSwitchView;public void setup() throws RuntimeException {/*** 1.表情管理工具<br/>* 2.表情容器<br/>* 3.表情开关按钮<br/>*/// IME managermInputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);// 表情管理mEmojiUtil = EmojiUtil.getInstance(getContext().getApplicationContext());// 放置弹出的容器mFrameContent = (FrameLayout) findViewById(R.id.fl_content);if (mFrameContent == null) {throw new RuntimeException("must specify a FrameLayout with id \"fl_content\" for putting content in");}//进行keyboard&&icon 切换mSwitchView = (ImageView) findViewById(R.id.iv_switch);if (mSwitchView == null) {throw new RuntimeException("must specify a View with id \"btn_emojis\"");}// bind listenermSwitchView.setOnClickListener(this);// 表情众面板mEmotionsPanel = new EmojiPanel(getContext());mEmotionsPanel.setOnEmotionClickListener(this);// 面板加入布局中,并默认不可见mEmotionsPanel.setVisibility(View.GONE);mFrameContent.addView(mEmotionsPanel);}public void onShowEmotionsPanel() {mEmotionsPanel.resetToFirstPage();mEmotionsPanel.setVisibility(View.VISIBLE);}public boolean isEmotionsPanelShown() {return mEmotionsPanel != null&& mEmotionsPanel.getVisibility() == View.VISIBLE;}public void hideEmotionsPanel() {mEmotionsPanel.setVisibility(View.GONE);}private void showKeyboard() {if (mInputMethodManager != null && mEditText != null) {mInputMethodManager.showSoftInput(mEditText,InputMethodManager.SHOW_IMPLICIT);}}private void hideSoftKeyInput() {if (mInputMethodManager != null && mEditText != null) {mInputMethodManager.hideSoftInputFromWindow(mEditText.getApplicationWindowToken(), 0);}}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.iv_switch: {if (isEmotionsPanelShown()) {hideEmotionsPanel();showKeyboard();mSwitchView.setImageResource(R.drawable.icon_icon);} else {hideSoftKeyInput();onShowEmotionsPanel();mSwitchView.setImageResource(R.drawable.keyboard);}break;}case R.id.et_input: {// 点击输入框->隐藏表情hideEmotionsPanel();break;}default:break;}}@Overridepublic void onEmotionClick(final String emojiFile) {if (mEditText == null) {return;}if (EmojiUtil.BACKSPACE.equals(emojiFile)) {Editable editable = mEditText.getText();// 删除光标所在的前一个字符或表情int index = mEditText.getSelectionStart();if (index <= 0) {// 光标在最前部,不需要删除return;}char c = editable.charAt(index - 1);if (']' == c) {String text = editable.toString();// 排除"[开心]A]"这种情况int nextOpenBracket = text.lastIndexOf('[', index - 2);int nextCloseBracket = text.lastIndexOf(']', index - 2);if (nextCloseBracket < nextOpenBracket) {// 删除一对儿editable.delete(nextOpenBracket, index);return;}}// 正常删除editable.delete(index - 1, index);} else {/** 点击了一个表情->在光标处插入表情*/// 将SpannableString插入到光标处int index = mEditText.getSelectionStart();mEditText.getText().insert(index,mEmojiUtil.getSpannableByEmojiName(getContext(), emojiFile));}}
}

4.键盘面板

public class EmojiPanel extends FrameLayout {...}

键盘面板就是一个自定义组件,在里面放入ViewPager&&添加一个小圆点,和常规的APP首页的轮播图是一个显示效果(这里还不需要自动滚动)。
讲一下小难点:

1.如果咱们将emoji表情都放在assets下面的emojis文件夹下面,如何遍历所有的文件呢?
2.如果获取assets文件夹中的某一文件呢?
这就涉及到assets文件的读写,请参考
读取assets文件
3.在每一页表情的最后一个是——删除按钮!也就是在遍历assets/emojis文件夹中的图片时,要将其过滤掉,最后直接放到最后一个按钮上。在计算将有多少页表情的时候

//mEmojiUtils.getEmojiNameArray()是不包含删除按钮的所有图片对应的图片名int pages = mEmojiUtils.getEmojiNameArray().length / (EMOJIS_PER_PAGE - 1);return mEmojiUtils.getEmojiNameArray().length % (EMOJIS_PER_PAGE - 1) == 0 ? pages : pages + 1;

在对每一个页面内的表情进行赋值时

 private String[] getArrayItems(int position) {String[] emojiNameList = mEmojiUtils.getEmojiNameArray();/* 判断当前页面要显示多少个表情position * (EMOJIS_PER_PAGE - 1): 是前面所有的表情页面已经显示的表情数目
*/
String[] array = new String[Math.min(EMOJIS_PER_PAGE, emojiNameList.length - position * (EMOJIS_PER_PAGE - 1))];for (int j = 0; j < array.length - 1; j++) {array[j] = emojiNameList[(position * (EMOJIS_PER_PAGE - 1) + j)];}/*array数组最后一个下标为 array.length - 1对于最后一个面板并不是  EMOJIS_PER_PAGE-1*/array[array.length - 1] = EmojiUtil.BACKSPACE;return array;}

处理用户点击表情事件

  @Overridepublic void onEmotionClick(final String emojiFile) {if (mEditText == null) {return;}
//通过文件名进行判断,如果为删除图标,执行删除操作if (EmojiUtil.BACKSPACE.equals(emojiFile)) {Editable editable = mEditText.getText();// 删除光标所在的前一个字符或表情int index = mEditText.getSelectionStart();if (index <= 0) {// 光标在最前部,不需要删除return;}char c = editable.charAt(index - 1);/*判断要删除的是不是表情为了将用户的正常输入与咱们的表情区分开来,这里使用[]对表情进行了包含(eg.emoji_001对应在文本框中的文本为"[emoji_001]")
*/
if (']' == c) {String text = editable.toString();// 排除"[开心]A]"这种情况int nextOpenBracket = text.lastIndexOf('[', index - 2);int nextCloseBracket = text.lastIndexOf(']', index - 2);if (nextCloseBracket < nextOpenBracket) {// 删除一对儿editable.delete(nextOpenBracket, index);return;}}//如果为正常文本,删除一个字符            editable.delete(index - 1, index);} else {/** 点击了一个表情->在光标处插入表情*/// 将SpannableString插入到光标处int index = mEditText.getSelectionStart();mEditText.getText().insert(index,mEmojiUtil.getSpannableByEmojiName(getContext(), emojiFile));}}

这里不但要让用户点击的文件名添加到文本上,记得要给它添加包装——[],

  /*** 根据emoji名字(如:emj_001)*/public SpannableString getSpannableByEmojiName(Context context,String iconName) {if (iconName.contains("[") && (iconName.contains("]"))) {iconName = iconName.substring(iconName.indexOf("[") + 1, iconName.indexOf("]"));}Bitmap emojiBmp = getEmojiIcon(iconName);SpannableString ss = new SpannableString("[" + iconName + "]");Drawable d = new BitmapDrawable(context.getResources(), emojiBmp);int emojiSize =Util.dip2px(context, 24);d.setBounds(0, 0, emojiSize, emojiSize);ImageSpan imgSpan = new ImageSpan(d);ss.setSpan(imgSpan, 0, iconName.length() + 2,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);return ss;}

表情对应字符串[emoji_001],还原为表情

  /*** 将纯文本,转换成带表情的Spannable*/public SpannableStringBuilder convert(Context context, CharSequence text) {if (TextUtils.isEmpty(text)) {return null;}SpannableStringBuilder builder = new SpannableStringBuilder(text);//通过正则表达式对emoji表情进行过滤Pattern p = Pattern.compile("\\[\\S+?\\]");Matcher m = p.matcher(builder);while (m.find()) {int start = m.start();String emojiName = m.group();// 用表情替换原名字builder.replace(start, start + emojiName.length(),getSpannableByEmojiName(context, emojiName));}return builder;}

下载地址

http://download.csdn.net/detail/guchuanhang/9564278

社会化APP加载表情的方法相关推荐

  1. webview 加载php页面内容,WebView加载优化的方法介绍

    本篇文章给大家带来的内容是关于WebView加载优化的方法介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. WebView加载优化 当WebView的使用频率变得频繁的时候,对于其 ...

  2. 【Android优化篇】提升Activity加载速度的方法

    文章转自:http://www.jianshu.com/p/2007ca0290d3 作者: CoderFan 前言 这个也是我面试遇到的问题,当时只回答了一种情况,异步加载数据,没想到别的方式,回来 ...

  3. 从app加载页面说开去

    好的交互设计的评判标准之一是"别让我等",但互联网产品总是受制于实际的网络问题.移动端产品则更为明显,2G.3G环境下加载不够给力,wifi环境也未必每次都是那么顺利.因此&quo ...

  4. vue 点击当前路由怎么重新加载_Vue 路由切换时页面内容没有重新加载的解决方法...

    第二次进入页面,页面路由参数已经改变,但是页面内容不会刷新. 问题原因:在组件mounted钩子中调用的刷新页面内容,但测试发现这个钩子没有被调用.后来发现App.vue中使用了: keep-aliv ...

  5. 免Root 实现App加载Xposed插件的工具Xpatch源码解析(一)

    前言 Xpatch是一款免Root实现App加载Xposed插件的工具,可以非常方便地实现App的逆向破解(再也不用改smali代码了),源码也已经上传到Github上,欢迎各位Fork and St ...

  6. android Viewpager取消预加载及Fragment方法的学习

    1.在使用ViewPager嵌套Fragment的时候,由于VIewPager的几个Adapter的设置来说,都会有一定的预加载.通过设置setOffscreenPageLimit(int numbe ...

  7. jquery跟js初始化加载的多种方法及区别介绍

    jquery是等待页面加载完数据,以及页面部分元素:js是页面全部加载完成才执行初始化加载,具体示例祥看本文 jquery和js初始化加载页面的区别:  jquery:等待页面加载完数据,以及页面部分 ...

  8. ios 旋转加载gif_加载GIF动画方法 iOS

    方法一 使用UIWebView _codeStr为gif网址      如果是本地的gif可以直接使用dataWithContentsOfFile方法 NSData *data = [NSData d ...

  9. ​​​​​​​CV:利用cv2(加载人脸识别xml文件及detectMultiScale函数得到人脸列表)+keras的load_model(加载表情hdf5、性别hdf5)并标注

    CV:利用cv2+自定义load_detection_model(加载人脸识别xml文件及detectMultiScale函数得到人脸列表)+keras的load_model(加载表情hdf5.性别h ...

最新文章

  1. mac环境下安装Gradle及配置
  2. 进入环境_大学新生,进入新环境该怎样和舍友、同学相处
  3. IO-03. 求整数均值
  4. 电商购物APP UI 模板素材,充满时尚感的设计
  5. spring api 中文_【每日3分钟技术干货 | 面试题+答案 | Springamp;SpringMVC篇
  6. 【白皮书分享】工业互联网人才白皮书(2020).pdf(附下载链接)
  7. windows 使用 tree 命令显示目录和文件
  8. 第十三届蓝桥杯省赛模拟赛题解(2022年第四次模拟赛)C/C++
  9. 前端实时可视化开发工具的使用
  10. 内容市场的2017年:五件大事,每件事都惊心动魄
  11. 前沿探索:腾讯云数据库自治服务最佳实现
  12. 最新杭州地铁开通时间表
  13. 转转验机源码+后台管理
  14. A beginning Flags
  15. windows和linux服务器哪个好?有哪些区别?
  16. 解决浏览器连不上校园网问题
  17. 通过修改hosts文件屏蔽网站的广告
  18. 【网盘项目日志】20210601:Seafile 离线下载系统开发(2)
  19. NOIP201205Vigenère密码
  20. 为什么学python就让我头秃

热门文章

  1. JVM原理之详解现代垃圾回收器 Shenandoah 和 ZGC
  2. Android Studio 4.1.1 项目编译失败 一直显示 Gradle Config Projecr
  3. USB显示器,USB Type-c /DP1.4 MST HUB芯片及方案
  4. 2020.01.12装机心得体会
  5. 2021-06-30软件测试day1学习笔记
  6. 9大行为导致Java程序员薪资过低, 你有几个?
  7. CSS 实现超过规定字符长度显示“...”
  8. CISA资格认证的前世今生
  9. 统计1到N(含)之间所有立方数的个数,并输出这个数目
  10. redis批量删除key命令