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
原创粉丝点击