android Emoji表情输入,输入表情和输入法表情输入相同

      首先我说一下我的大致经过,因为公司是做时尚类的所有表情的做法直接模仿了小红书和好,没有做全部的表情代码,只做了80个常用表情,表情图片自己去准备。因为设计刚好离职的缘故,所以自己偷懒,直接解压了nice的apk包,然后采用了nice的表情图片(64 * 64px)。然后有两种做法,一种是edittext编辑的时候插入对应的表情图片,用到imagespan,然后自己跟服务端匹配了对应的表情标识(各种转码...超级麻烦)。然后做完之后发现图片不论我怎么剪裁,都不能完美的匹配字体大小,具体情况就是输入表情时输入框文字高度会出现下移,具体原因没有找到,后来我用搜狗输入法的表情输入之后,发现它为什么能完全的匹配,而且服务端不需要任何匹配,它也可以正常的显示,于是我想找到它是怎么做的,然后各种查找如何调用输入法的emoji表情,结果无奈并没有找到结果。于是我想是不是系统自己做了某些处理,因为android系统本身也有有表情的,那么输入法也是调用的系统的表情,是在我就先找到了unicode码大全 表情符号表  ,找到你需要的unicode码,然后用集合封装起来,与你的emoji表情对应。我是直接建了一个emoji工具类,用来封装emoji对象。

public class EmojiUtil {

    private static ArrayList  mEmojiList;

    //获取emoji表情集合
    public static ArrayList  getEmojiList(){
        if(mEmojiList == null){
            mEmojiList = generateEmojis();
        }}
        return mEmojiList;
    }}

    private static ArrayList  generateEmojis(){
        ArrayList  list = new ArrayList <>();
        for(int i = 0; i 
 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 ViewPagerItems = new ArrayList<>();
    ArrayList 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 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 list) {
        faceIndicator.init(getPagerCount(list));
    }


    /**
     * 根据表情数量以及GridView设置的行数和列数计算Pager数量
     *
     * @return
     */
    private int getPagerCount(ArrayList 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 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 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 list;
        private Context mContext;

        public FaceGVAdapter(List 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 views;

        public FaceVPAdapter(List 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 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,重要的事说三遍,如果你真的需要,肯定也会自己研究一下吧。

 
  

 
  
 
 

你可能感兴趣的:(Emoji辅助类)