android Emoji表情输入,输入表情和输入法表情输入相同
来源:互联网 发布:mac压缩包 编辑:程序博客网 时间:2024/04/27 13:00
首先我说一下我的大致经过,因为公司是做时尚类的所有表情的做法直接模仿了小红书和好,没有做全部的表情代码,只做了80个常用表情,表情图片自己去准备。因为设计刚好离职的缘故,所以自己偷懒,直接解压了nice的apk包,然后采用了nice的表情图片(64 * 64px)。然后有两种做法,一种是edittext编辑的时候插入对应的表情图片,用到imagespan,然后自己跟服务端匹配了对应的表情标识(各种转码...超级麻烦)。然后做完之后发现图片不论我怎么剪裁,都不能完美的匹配字体大小,具体情况就是输入表情时输入框文字高度会出现下移,具体原因没有找到,后来我用搜狗输入法的表情输入之后,发现它为什么能完全的匹配,而且服务端不需要任何匹配,它也可以正常的显示,于是我想找到它是怎么做的,然后各种查找如何调用输入法的emoji表情,结果无奈并没有找到结果。于是我想是不是系统自己做了某些处理,因为android系统本身也有有表情的,那么输入法也是调用的系统的表情,是在我就先找到了unicode码大全 表情符号表 ,找到你需要的unicode码,然后用集合封装起来,与你的emoji表情对应。我是直接建了一个emoji工具类,用来封装emoji对象。
public class EmojiUtil { private static ArrayList <Emoji> mEmojiList; //获取emoji表情集合 public static ArrayList <Emoji> getEmojiList(){ if(mEmojiList == null){ mEmojiList = generateEmojis(); }} return mEmojiList; }} private static ArrayList <Emoji> generateEmojis(){ ArrayList <Emoji> list = new ArrayList <>(); for(int i = 0; i <EmojiResArray.length; i ++){ Emoji emoji = new Emoji(); emoji.setEmojiResId(EmojiResArray [i]); emoji.setEmojiUnicode(EmojiUnicodeArray [i]); list.add(emoji); }} 返回列表; }} public static final int [] EmojiUnicodeArray = { 0x1f604, 0x1f603, 0x1f60a, 0x1f609, 0x1f60d, 0x1f618, 0x1f61a, 0x1f61c, 0x1f61d, 0x1f633, 0x1f601, 0x1f614, 0x1f60c, 0x1f612, 0x1f61e, 0x1f623, 0x1f622, 0x1f602, 0x1f62d, 0x1f61a, // ------------------------------- 0x1f625, 0x1f630, 0x1f613, 0x1f628, 0x1f631, 0x1f620, 0x1f621, 0x1f616, 0x1f637, 0x1f632, 0x1f47f, 0x1f60f, 0x1f466, 0x1f467, 0x1f468, 0x1f469, 0x1f31f, 0x1f444, 0x1f44d, 0x1f44e, // ------------------------------- 0x1f44c, 0x1f44a, 0x270a, 0x270c, 0x1f446, 0x1f447, 0x1f449, 0x1f448, 0x1f64f, 0x1f44f, 0x1f4aa, 0x1f457, 0x1f380, 0x2764, 0x1f494, 0x1f48e, 0x1f436, 0x1f431, 0x1f339, 0x1f33b, // ------------------------------- 0x1f341, 0x1f343, 0x1f319, 0x2600, 0x2601, 0x26a1, 0x2614, 0x1f47b, 0x1f385, 0x1f381, 0x1f4f1, 0x1f50d, 0x1f4a3, 0x26bd, 0x2615, 0x1f37a, 0x1f382, 0x1f3e0, 0x1f697, 0x1f559, }; public static final int [] EmojiResArray = { R.drawable.emoji_0x1f604, R.drawable.emoji_0x1f603, R.drawable.emoji_0x1f60a, R.drawable.emoji_0x1f609, R.drawable.emoji_0x1f60d, R.drawable.emoji_0x1f618, R.drawable.emoji_0x1f61a, R.drawable.emoji_0x1f61c, R.drawable.emoji_0x1f61d, R.drawable.emoji_0x1f633, R.drawable.emoji_0x1f601, R.drawable.emoji_0x1f614, R.drawable.emoji_0x1f60c, R.drawable.emoji_0x1f612, R.drawable.emoji_0x1f61e, R.drawable.emoji_0x1f623, R.drawable.emoji_0x1f622, R.drawable.emoji_0x1f602, R.drawable.emoji_0x1f62d, R.drawable.emoji_0x1f61a, // ------------------------------- R.drawable.emoji_0x1f625, R.drawable.emoji_0x1f630, R.drawable.emoji_0x1f613, R.drawable.emoji_0x1f628, R.drawable.emoji_0x1f631, R.drawable.emoji_0x1f620, R.drawable.emoji_0x1f621, R.drawable.emoji_0x1f616, R.drawable.emoji_0x1f637, R.drawable.emoji_0x1f632, R.drawable.emoji_0x1f47f, R.drawable.emoji_0x1f60f, R.drawable.emoji_0x1f466, R.drawable.emoji_0x1f467, R.drawable.emoji_0x1f468, R.drawable.emoji_0x1f469, R.drawable.emoji_0x1f31f, R.drawable.emoji_0x1f444, R.drawable.emoji_0x1f44d, R.drawable.emoji_0x1f44e, // ------------------------------- R.drawable.emoji_0x1f44c, R.drawable.emoji_0x1f44a, R.drawable.emoji_0x270a, R.drawable.emoji_0x270c, R.drawable.emoji_0x1f446, R.drawable.emoji_0x1f447, R.drawable.emoji_0x1f449, R.drawable.emoji_0x1f448, R.drawable.emoji_0x1f64f, R.drawable.emoji_0x1f44f, R.drawable.emoji_0x1f4aa, R.drawable.emoji_0x1f457, R.drawable.emoji_0x1f380, R.drawable.emoji_0x2764, R.drawable.emoji_0x1f494, R.drawable.emoji_0x1f48e, R.drawable.emoji_0x1f436, R.drawable.emoji_0x1f431, R.drawable.emoji_0x1f339, R.drawable.emoji_0x1f33b, // ------------------------------- R.drawable.emoji_0x1f341, R.drawable.emoji_0x1f343, R.drawable.emoji_0x1f319, R.drawable.emoji_0x2600, R.drawable.emoji_0x2601, R.drawable.emoji_0x26a1, R.drawable.emoji_0x2614, R.drawable.emoji_0x1f47b, R.drawable.emoji_0x1f385, R.drawable.emoji_0x1f381, R.drawable.emoji_0x1f4f1, R.drawable.emoji_0x1f50d, R.drawable.emoji_0x1f4a3, R.drawable.emoji_0x26bd, R.drawable.emoji_0x2615, R.drawable.emoji_0x1f37a, R.drawable.emoji_0x1f382, R.drawable.emoji_0x1f3e0, R.drawable.emoji_0x1f697, R.drawable.emoji_0x1f559, }; public static int dip2px(Context context,float dipValue){ final float scale = context.getResources()。getDisplayMetrics()。density; return(int)(dipValue * scale + 0.5f); }}static { mEmojiList = generateEmojis(); } public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // 源图片的高度和宽度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // 计算出实际宽高和目标宽高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 // 一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小 final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // 调用上面定义的方法计算inSampleSize值 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 使用获取到的inSampleSize值再次解析图片 options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } // 缩放图片 public static Bitmap zoomImg(Bitmap bm, int newWidth, int newHeight) { // 获得图片的宽高 int width = bm.getWidth(); int height = bm.getHeight(); // 计算缩放比例 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 取得想要缩放的matrix参数 Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); // 得到新的图片 Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true); return newbm; } public static Bitmap getBitmapFromRes(Resources res, int resId, int reqWidth, int reqHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeResource(res, resId, options); return zoomImg(bitmap, reqWidth, reqHeight); } /** * 通过unicode码转换成字符串 * * @param unicode * @return */ public static String getEmojiStringByUnicode(int unicode) { return new String(Character.toChars(unicode)); }
emoji表情的实体bean,就两个元素public class Emoji implements Serializable { // emoji图片资源id private int emojiResId; // emoji代表的utf-8代码 private int emojiUnicode; public int getEmojiResId(){ return emojiResId; }} public void setEmojiResId(int emojiResId){ this.emojiResId = emojiResId; }} public int getEmojiUnicode(){ return emojiUnicode; }} public void setEmojiUnicode(int emojiUnicode){ this.emojiUnicode = emojiUnicode; }}}}
然后你需要的就是一个emoji选择框,直接用片段写,然后初始化对应的表情,适配器,然后对外提供添加表情和删除表情的方法接口。然后你点击表情的时候将对应表情unicode码转成字符添加到文本内容@Override public void onEmojiDelete() { String text = mInputComment.getText().toString(); if (text.isEmpty()) { return; } int action = KeyEvent.ACTION_DOWN; int code = KeyEvent.KEYCODE_DEL; KeyEvent event = new KeyEvent(action, code); mInputComment.onKeyDown(KeyEvent.KEYCODE_DEL, event); } @Override public void onEmojiClick(Emoji emoji) { LogUtil.e("点击了emoji=" + emoji.getEmojiUnicode()); if (emoji != null) { int index = mInputComment.getSelectionStart(); Editable editable = mInputComment.getEditableText(); if (index < 0 || index >= editable.length()) { editable.append(EmojiUtil.getEmojiStringByUnicode(emoji.getEmojiUnicode())); } else { editable.insert(index, EmojiUtil.getEmojiStringByUnicode(emoji.getEmojiUnicode())); } } }最后关于表情页面片段我没有具体写,直接把github上面的大神写的拿过来用了,你也可以参考一下public class FaceFragment extends Fragment { public static FaceFragment Instance() { FaceFragment instance = new FaceFragment(); Bundle bundle = new Bundle(); instance.setArguments(bundle); return instance; } ViewPager faceViewPager; EmojiIndicatorView faceIndicator; ArrayList<View> ViewPagerItems = new ArrayList<>(); ArrayList<Emoji> emojiList; private int columns = 7; private int rows = 3; private OnEmojiClickListener listener; public void setListener(OnEmojiClickListener listener) { this.listener = listener; } @Override public void onAttach(Activity activity) { if (activity instanceof OnEmojiClickListener) { this.listener = (OnEmojiClickListener) activity; } super.onAttach(activity); } @Override public void onCreate(Bundle savedInstanceState) { emojiList = EmojiUtil.getEmojiList(); super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_face, container, false); faceViewPager = (ViewPager) view.findViewById(R.id.face_viewPager); faceIndicator = (EmojiIndicatorView) view.findViewById(R.id.face_indicator); initViews(); return view; } private void initViews() { initViewPager(emojiList); } private void initViewPager(ArrayList<Emoji> list) { intiIndicator(list); ViewPagerItems.clear(); for (int i = 0; i < getPagerCount(list); i++) { ViewPagerItems.add(getViewPagerItem(i, list)); } FaceVPAdapter mVpAdapter = new FaceVPAdapter(ViewPagerItems); faceViewPager.setAdapter(mVpAdapter); faceViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { int oldPosition = 0; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { faceIndicator.playBy(oldPosition, position); oldPosition = position; } @Override public void onPageScrollStateChanged(int state) { } }); } private void intiIndicator(ArrayList<Emoji> list) { faceIndicator.init(getPagerCount(list)); } /** * 根据表情数量以及GridView设置的行数和列数计算Pager数量 * * @return */ private int getPagerCount(ArrayList<Emoji> list) { int count = list.size(); return count % (columns * rows - 1) == 0 ? count / (columns * rows - 1) : count / (columns * rows - 1) + 1; } private View getViewPagerItem(int position, ArrayList<Emoji> list) { LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View layout = inflater.inflate(R.layout.layout_face_grid, null);//表情布局 GridView gridview = (GridView) layout.findViewById(R.id.chart_face_gv); /** * 注:因为每一页末尾都有一个删除图标,所以每一页的实际表情columns * rows - 1; 空出最后一个位置给删除图标 * */ final List<Emoji> subList = new ArrayList<>(); subList.addAll(list.subList(position * (columns * rows - 1), (columns * rows - 1) * (position + 1) > list .size() ? list.size() : (columns * rows - 1) * (position + 1))); /** * 末尾添加删除图标 * */ if (subList.size() < (columns * rows - 1)) { for (int i = subList.size(); i < (columns * rows - 1); i++) { subList.add(null); } } Emoji deleteEmoji = new Emoji(); deleteEmoji.setEmojiResId(R.drawable.emoji_delete); subList.add(deleteEmoji); FaceGVAdapter mGvAdapter = new FaceGVAdapter(subList, getActivity()); gridview.setAdapter(mGvAdapter); gridview.setNumColumns(columns); // 单击表情执行的操作 gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (position == columns * rows - 1) { if (listener != null) { listener.onEmojiDelete(); } return; } if (listener != null) { listener.onEmojiClick(subList.get(position)); } } }); return gridview; } @Override public void onDestroyView() { super.onDestroyView(); } class FaceGVAdapter extends BaseAdapter { private List<Emoji> list; private Context mContext; public FaceGVAdapter(List<Emoji> list, Context mContext) { super(); this.list = list; this.mContext = mContext; } @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return list.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(mContext).inflate(R.layout.item_face, null); holder.iv = (ImageView) convertView.findViewById(R.id.face_image); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } if (list.get(position) != null) { holder.iv.setImageBitmap(EmojiUtil.decodeSampledBitmapFromResource(getActivity().getResources(), list.get(position).getEmojiResId(), EmojiUtil.dip2px(getActivity(), 32), EmojiUtil.dip2px(getActivity(), 32))); } return convertView; } class ViewHolder { ImageView iv; } } class FaceVPAdapter extends PagerAdapter { // 界面列表 private List<View> views; public FaceVPAdapter(List<View> views) { this.views = views; } @Override public void destroyItem(View arg0, int arg1, Object arg2) { ((ViewPager) arg0).removeView((View) (arg2)); } @Override public int getCount() { return views.size(); } // 初始化arg1位置的界面 @Override public Object instantiateItem(View arg0, int arg1) { ((ViewPager) arg0).addView(views.get(arg1)); return views.get(arg1); } // 判断是否由对象生成界 @Override public boolean isViewFromObject(View arg0, Object arg1) { return (arg0 == arg1); } } public interface OnEmojiClickListener { void onEmojiDelete(); void onEmojiClick(Emoji emoji); }}
好了表情问题已经解决了,最后只剩下输入法的问题了,相信看过小红书的都知道如果用输入框可自动伸缩的模式的话,切换表情和输入模式时会有闪动的情况,而你如果细心的注意微信的话,你会发现微信居然没有这个,我猜他应该是用了覆盖的方法,怎么说呢,嗯,relativelayout的那种,下面是表情框,只要输入框获取焦点,他就展开,也就是说只要输入框有焦点,表情狂一直是存在的,只是控制输入法的折叠与展开而已,但是新的问题来了,你怎么保证输入法高度刚好遮盖住表情呢?那就需要输入法测量高度了,这里用到了一个新的类public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener { public interface SoftKeyboardStateListener { void onSoftKeyboardOpened(int keyboardHeightInPx); void onSoftKeyboardClosed(); } private final List<SoftKeyboardStateListener> listeners = new LinkedList<>(); private final View activityRootView; private int lastSoftKeyboardHeightInPx; private boolean isSoftKeyboardOpened; public SoftKeyboardStateWatcher(View activityRootView) { this(activityRootView, false); } public SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) { this.activityRootView = activityRootView; this.isSoftKeyboardOpened = isSoftKeyboardOpened; activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this); } @Override public void onGlobalLayout() { final Rect r = new Rect(); //r will be populated with the coordinates of your view that area still visible. activityRootView.getWindowVisibleDisplayFrame(r); final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top); //此处判断键盘弹出高度的时候需要考虑部分机型(如:华为,魅族等)虚拟键盘的高度 int virtualKeyHeight = ScreenUtil.getVirtualKeyHeight(); // if more than 100 pixels, its probably a keyboard... if (!isSoftKeyboardOpened && heightDiff >= (virtualKeyHeight + 100)) { isSoftKeyboardOpened = true; notifyOnSoftKeyboardOpened(heightDiff); } else if (isSoftKeyboardOpened && heightDiff < (virtualKeyHeight + 100)) { isSoftKeyboardOpened = false; notifyOnSoftKeyboardClosed(); } } public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) { this.isSoftKeyboardOpened = isSoftKeyboardOpened; } public boolean isSoftKeyboardOpened() { return isSoftKeyboardOpened; } /** * Default value is zero {@code 0}. * * @return last saved keyboard height in px */ public int getLastSoftKeyboardHeightInPx() { return lastSoftKeyboardHeightInPx; } public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) { listeners.add(listener); } public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) { listeners.remove(listener); } private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) { this.lastSoftKeyboardHeightInPx = keyboardHeightInPx; for (SoftKeyboardStateListener listener : listeners) { if (listener != null) { listener.onSoftKeyboardOpened(keyboardHeightInPx); } } } private void notifyOnSoftKeyboardClosed() { for (SoftKeyboardStateListener listener : listeners) { if (listener != null) { listener.onSoftKeyboardClosed(); } } }}这个东西知道吧,输入法的监听器,我遇到了虚拟键盘的问题,所以稍加了改动。他就可以用来监听输入法的高度了,你获取到输入法高度之后永久记录下来然后以后动态设置给表情框,你会问如果我第一次打开表情怎么办?对,这确实是个问题,没什么解决办法,只能设置一个默认值了,然后啥时候监听到输入法的高度了之后再保存吧!好了所有的内容都在这了,希望对你有帮助。本来发图片的结果大小受限,就不发了。最后声明一下,没有demo,没有demo,没有demo,重要的事说三遍,如果你真的需要,肯定也会自己研究一下吧。
1 0
- android Emoji表情输入,输入表情和输入法表情输入相同
- emoji 表情过滤 解决 sogo 输入法输入表情 服务器不支持
- iOS禁止输入emoji表情
- iOS禁止输入emoji表情
- MySql之输入Emoji表情
- Editext禁止输入Emoji表情
- textarea禁止输入emoji表情
- iOS输入框禁止输入emoji表情
- android 输入表情icon
- android开发表情输入
- Android EditText 不能输入 手机输入法自带 Emoji 表情,为EditText 添加输入限制
- android 输入框EditText禁止输入Emoji表情符
- android 输入框EditText禁止输入Emoji表情符
- 【Android】【输入校验】输入框(EditText)过滤 Emoji 表情
- android 输入框EditText禁止输入Emoji表情符
- android 输入框EditText禁止输入Emoji表情符
- android 输入框EditText禁止输入Emoji表情符
- android 输入框EditText禁止输入Emoji表情符
- OpenSceneGraph 3.0.1 生成 chm 文档
- 键盘过滤驱动
- Programming Assignment 3: Pattern Recognition
- MATLAB文件I/O操作——新建TXT并写入数据
- UVa_Trees on the level(BFS)
- android Emoji表情输入,输入表情和输入法表情输入相同
- Oracle视图
- 浅析Oracle查看执行计划的三种方式
- 源码:JAVA在线编译器模拟
- banner实现广告的无线轮播
- 学习hector_slam(三)随手记录:hector_mapping
- React Native之React速学教程(下)
- spring MVC到处Excel文件
- redis set