美文网首页
Android 表情功能

Android 表情功能

作者: whstywh | 来源:发表于2017-07-25 14:17 被阅读0次

    思路:
    表情图片资源
    表情展示面板
    输入删除逻辑


    首先需要下载表情相关资源,链接:http://pan.baidu.com/s/1pLLTEkV 密码:etow,下载完后将图片添加到项目中。
    然后自定义一个工具类,作用一:使得表情图片的文件名和在项目中的资源id作为map集合的键值对一一对应。

    public static final int[] EmojiResArray = {
                R.drawable.d_aini,
                R.drawable.d_aoteman,
                R.drawable.d_baibai,
                ...
                R.drawable.w_yueliang,
        };
    
      public static final String[] EmojiTextArray = {
                "[爱你]",
                "[奥特曼]",
                "[拜拜]",
                ...
                "[月亮]",
        };
    
        private static final Map<String, Integer> EmojiMap = new HashMap<>();
    
        static {
            for (int i = 0; i < EmojiResArray.length; i++) {
                EmojiMap.put(EmojiTextArray[i], EmojiResArray[i]);
            }
        }
    
       public static int getImgByName(String key) {
            return EmojiMap.get(key);
        }
    

    作用二:用于将服务器返回的类似[爱你][奥特曼]格式的字符串转换成对应表情图片的复合文本。

      //正则表达式匹配,[爱你][奥特曼]——> 表情的复合文本
        public static SpannableString getEmotionContent(final Context context, final TextView tv, String source) {
            SpannableString spannableString = new SpannableString(source);
            Resources res = context.getResources();
    
            String regexEmotion = "\\[([\u4e00-\u9fa5\\w])+\\]";
            Pattern patternEmotion = Pattern.compile(regexEmotion);
            Matcher matcherEmotion = patternEmotion.matcher(spannableString);
    
            while (matcherEmotion.find()) {
                // 获取匹配到的具体字符
                String key = matcherEmotion.group();
                // 匹配字符串的开始位置
                int start = matcherEmotion.start();
                // 利用表情名字获取到对应的图片
                Integer imgRes = EmojiUtils.getImgByName(key);
                if (imgRes != null) {
                    // 压缩表情图片
                    int size = (int) tv.getTextSize();
                    Bitmap bitmap = BitmapFactory.decodeResource(res, imgRes);
                    Bitmap scaleBitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);
    
                    ImageSpan span = new ImageSpan(context, scaleBitmap);
                    spannableString.setSpan(span, start, start + key.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                }
            }
            return spannableString;
        }
    

    表情面板布局:一个Fragment上填充一个ViewPager容器,ViewPager每一页都是一个3*7的GridView布局,其中每一个都是一张表情图片,并为其添加点击事件。
    EViewPagerAdapter和EGridViewAdapter的具体实现代码不再赘述,需要注意的是他们的数据源分别是以GridView为泛型的和Integer为泛型的list集合。
    EmojiFragment中需要自定义一个内部类接口,通过接口回调返回点击的表情的顺序序号,从而获取对应的文字名

        private void init() {
    
            List<GridView> gridViewList = new ArrayList<>();
            EmojiGridViewAdapter gridViewAdapter = null;
            for (int i = 0; i < EmojiUtils.EmojiResArray.length; i++) {
                if (i % 20 == 0) {
                    final int po = i;
                    GridView view = new GridView(getContext());
                    view.setNumColumns(7);
                    view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                        @Override
                        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                            if (po >= 20) {
                                mOnClickOnFragment.onClickFragment(position + po);
                            } else {
                                mOnClickOnFragment.onClickFragment(position);
                            }
                        }
                    });
                    gridViewAdapter = new EmojiGridViewAdapter();
                    gridViewAdapter.addInt(getContext(), EmojiUtils.EmojiResArray[i]);
                    view.setAdapter(gridViewAdapter);
                    gridViewList.add(view);
                    continue;
                }
                gridViewAdapter.addInt(getContext(), EmojiUtils.EmojiResArray[i]);
            }
    
            EmojiViewPagerAdapter adapter = new EmojiViewPagerAdapter();
            adapter.setGridViews(gridViewList);
            mEmojiViewPager.setAdapter(adapter);
        }
    
    
        public interface OnClickOnFragment {
            void onClickFragment(int i);
        }
    

    使用时只需将该EmojiFragment表情面板填充到布局中即可,

     //填充表情到布局中
            EmojiFragment emojiFragment = new EmojiFragment();
            getSupportFragmentManager().beginTransaction().add(R.id.emojiLayout, emojiFragment).commit();
    

    输入删除逻辑思路:当我们点击表情面板来输入表情时,根据接口回调的int值得到相应的表情图片id,然后以复合文本的形式在EditText中显示,同时会对表情图片进行压缩使其大小和输入的文字一般大,

    
        //根据数组中position获得相对应的表情的复合文本
        public static SpannableString getEmotionEditText(Context context, EditText editText, int position) {
            SpannableString spanString = new SpannableString(" ");
            int size = (int) editText.getTextSize();
            Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), EmojiUtils.EmojiResArray[position]);
            Bitmap scaleBitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);
            ImageSpan span = new ImageSpan(context, scaleBitmap);
            spanString.setSpan(span, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            return spanString;
        }
    

    但是不会将该复合文本提交服务器,而是将表情图片对应的文件名以"[爱你]"的格式拼接到mCommitString字符串中,然后才提交服务器。
    需要注意的是:一个表情图片的复合文本添加到EditText中只占一个字符,但是需要拼接的[爱你]确实四个字符串,这样是如何在EditText中任意输入表情时,mCommitString字符串是如何在准确位置拼接的?

    定义一个泛型是Integer的list集合,该集合的索引值看做是mEdiText中的字符的排列位置,同时也是mCommitString字符串中字符的排列位置,该集合的value值则是该位置在mCommitString字符串中的长度,比如:"我[爱你]"在EditText中第2个位置是一个表情的复合文本,只占1个字符,但是在mCommitString字符串中第2个位置确实4个字符,此时该集合索引为1时,value值为1,索引为2时,value值为4;

    //表情的点击监听事件
            emojiFragment.setOnClickOnFragment(new EmojiFragment.OnClickOnFragment() {
                @Override
                public void onClickFragment(int i) {
    //鼠标光标选中的位置
                    int selectionStart = mEditText.getSelectionStart();
    //EditText中添加复合文本
                    SpannableString emotionEditText = EmojiUtils.getEmotionEditText(SendCardDetailsActivity.this, mEditText, i);
                    mEditText.getText().insert(selectionStart, emotionEditText);
                    mList.add(selectionStart, EmojiUtils.EmojiTextArray[i].length());
                    int x = 0;
                    for (int i1 = 0; i1 < selectionStart; i1++) {
                        if (mList.get(i1) != null) {
                            x += mList.get(i1);
                        }
                    }
                    LogUtils.d(TAG, "x:" + x);
                    mCommitString.insert(x, EmojiUtils.EmojiTextArray[i].trim());
                }
    

    当在EditText的任意位置插入的不是表情而是空格,汉字,字母时也面临这一个问题,就是在EditText的某一位置插入一个字符时,同时需要在mCommitString字符串准确插入该字符,由于在某一位置是一对多的关系,就很有可能造成提交的字符串位置错乱,比如向"我[爱你]的后面插入"呀"字,就有可能出现"我[呀爱你]"这种现象;
    解决办法就是:在EditText中插入字符时,得到插入的初始位置,然后遍历累加小于该初始值所有索引的value,得到的就是mCommitString字符串中将要插入的准确初始位置,删除也是同样原理,初始位置和结束位置之间索引对应的value值累加就是将要删除的真正长度,

     //内容EditText 文本改变监听
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }
    
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            LogUtils.d(TAG, start + ":" + before + ";" + count + "");
            int y1 = 0;
            int y2 = 0;
            int x1 = 0;
            int x2 = start;
    //count ==0 时是删除操作
            if (count == 0) {
                for (int i = 0; i <= start; i++) {
                    if (mList.get(i) != null) {
                        y1 += mList.get(i);
                    }
                }
                for (int i = 0; i <= start - before; i++) {
                    if (mList.get(i) != null) {
                        y2 += mList.get(i);
                    }
                }
                LogUtils.d(TAG, "y1:" + y1);
                LogUtils.d(TAG, "y2:" + y2);
                mCommitString.delete(y2, y1);
            } else {
    //如果是一次输入一大段汉字,此时就不是一对多的关系,EditText和`mCommitString`字符串就是一对一的关系,在集合中插入大段字符串的长度的索引区间的value值都赋值为1,就保证集合长度和字符串长度一致
                for (int i = 0; i < count; i++) {
                    mList.add(x2++, 1);
                }
                for (int i = 0; i < start; i++) {
                    if (mList.get(i) != null) {
                        x1 += mList.get(i);
                    }
                }
                LogUtils.d(TAG, "x1:" + x1);
    //当是输入表情时,忽略空格;否则允许输入空格
                if (mIsEmoji) {
                    mCommitString.insert(x1, s.toString().substring(start, start + count).trim());
                } else
                    mCommitString.insert(x1, s.toString().substring(start, start + count));
                mIsEmoji = false;
    
            }
            LogUtils.d(TAG, mCommitString.toString());
        }
    
        @Override
        public void afterTextChanged(Editable s) {
    
        }
    

    笔者水平有限,希望大家教我

    相关文章

      网友评论

          本文标题:Android 表情功能

          本文链接:https://www.haomeiwen.com/subject/iaehkxtx.html