Android 表情功能

思路:
表情圖片資源
表情展示面板
輸入刪除邏輯


首先需要下載表情相關(guān)資源,鏈接:http://pan.baidu.com/s/1pLLTEkV 密碼:etow,下載完后將圖片添加到項目中。
然后自定義一個工具類,作用一:使得表情圖片的文件名和在項目中的資源id作為map集合的鍵值對一一對應(yīng)。

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);
    }

作用二:用于將服務(wù)器返回的類似[愛你][奧特曼]格式的字符串轉(zhuǎn)換成對應(yīng)表情圖片的復(fù)合文本。

  //正則表達(dá)式匹配,[愛你][奧特曼]——> 表情的復(fù)合文本
    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();
            // 利用表情名字獲取到對應(yīng)的圖片
            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的具體實現(xiàn)代碼不再贅述,需要注意的是他們的數(shù)據(jù)源分別是以GridView為泛型的和Integer為泛型的list集合。
EmojiFragment中需要自定義一個內(nèi)部類接口,通過接口回調(diào)返回點擊的表情的順序序號,從而獲取對應(yīng)的文字名

    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();

輸入刪除邏輯思路:當(dāng)我們點擊表情面板來輸入表情時,根據(jù)接口回調(diào)的int值得到相應(yīng)的表情圖片id,然后以復(fù)合文本的形式在EditText中顯示,同時會對表情圖片進(jìn)行壓縮使其大小和輸入的文字一般大,


    //根據(jù)數(shù)組中position獲得相對應(yīng)的表情的復(fù)合文本
    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;
    }

但是不會將該復(fù)合文本提交服務(wù)器,而是將表情圖片對應(yīng)的文件名以"[愛你]"的格式拼接到mCommitString字符串中,然后才提交服務(wù)器。
需要注意的是:一個表情圖片的復(fù)合文本添加到EditText中只占一個字符,但是需要拼接的[愛你]確實四個字符串,這樣是如何在EditText中任意輸入表情時,mCommitString字符串是如何在準(zhǔn)確位置拼接的?

定義一個泛型是Integer的list集合,該集合的索引值看做是mEdiText中的字符的排列位置,同時也是mCommitString字符串中字符的排列位置,該集合的value值則是該位置在mCommitString字符串中的長度,比如:"我[愛你]"在EditText中第2個位置是一個表情的復(fù)合文本,只占1個字符,但是在mCommitString字符串中第2個位置確實4個字符,此時該集合索引為1時,value值為1,索引為2時,value值為4;

//表情的點擊監(jiān)聽事件
        emojiFragment.setOnClickOnFragment(new EmojiFragment.OnClickOnFragment() {
            @Override
            public void onClickFragment(int i) {
//鼠標(biāo)光標(biāo)選中的位置
                int selectionStart = mEditText.getSelectionStart();
//EditText中添加復(fù)合文本
                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());
            }

當(dāng)在EditText的任意位置插入的不是表情而是空格,漢字,字母時也面臨這一個問題,就是在EditText的某一位置插入一個字符時,同時需要在mCommitString字符串準(zhǔn)確插入該字符,由于在某一位置是一對多的關(guān)系,就很有可能造成提交的字符串位置錯亂,比如向"我[愛你]的后面插入"呀"字,就有可能出現(xiàn)"我[呀愛你]"這種現(xiàn)象;
解決辦法就是:在EditText中插入字符時,得到插入的初始位置,然后遍歷累加小于該初始值所有索引的value,得到的就是mCommitString字符串中將要插入的準(zhǔn)確初始位置,刪除也是同樣原理,初始位置和結(jié)束位置之間索引對應(yīng)的value值累加就是將要刪除的真正長度,

 //內(nèi)容EditText 文本改變監(jiān)聽
    @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 {
//如果是一次輸入一大段漢字,此時就不是一對多的關(guān)系,EditText和`mCommitString`字符串就是一對一的關(guān)系,在集合中插入大段字符串的長度的索引區(qū)間的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);
//當(dāng)是輸入表情時,忽略空格;否則允許輸入空格
            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) {

    }

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,048評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評論 19 139
  • 目標(biāo)·生活
    覃小初閱讀 162評論 0 0
  • 我想做一朵云 一朵你知道的云 風(fēng)起時,看你的樣子 雨落時,做你的傘 我想做一朵云 一朵知道你的云 你餓時,做你的棉...
    四斤閱讀 1,097評論 1 6
  • 目錄 連續(xù)好幾天,我都被逼充當(dāng)考霸答案搬運工,成功讓丫頭在大學(xué)的最后一年也當(dāng)了一回考霸。 好不容易終于考完試了,我...
    lekli閱讀 678評論 0 6

友情鏈接更多精彩內(nèi)容