1. 程式人生 > >Android開發中自定義表情併發送出去之經典的傳送表情

Android開發中自定義表情併發送出去之經典的傳送表情

本文例項講述了Android程式設計開發實現輸入(自定義表情包)QQ表情影象併發送出去別人收到並解析出來的方法。分享給大家供大家參考,原來QQ微信等傳送表情其實發送的都不是表情,而是一個富文字,收到訊息後再解析得來的,具體效果如下

表情傳送出去是這樣:

 

最近在開發的一個專案是即時通訊專案,像QQ,微信等功能的專案,其中我負責的一個模組中含有表情傳送的

功能,具體需求如下:

1:自定義我們自己的表情包

2:在輸入框旁邊點選頭像按鈕像QQ微信一樣跳出很多表情供我們選擇

3:點選表情在輸入框中顯示,可以和文字混合排列。

*************************************************這是個分界線***************************************************************

4:點擊發送通過伺服器發出去

5:對方收到文字和表情的資訊(注意這裡是表情而不是一個符號或者文字),並且可以正常交流。

***********************************************這又是一個分界線************************************************************

6:在輸入表情的時候會出現在文字中間插入表情的時候就會插入到最後,但是已經解決了。

下面開始我的開發步驟:

因為之前沒有遇到這樣的問題,網上也比較少的講述,也是查閱了很久結合自己的感悟,其中上面的分界線是卡著

我兩天的地方,所以做一個分界線,紀念一下,好了開始正題:

1:首先找到表情圖片的資源,圖片截圖如下:

2:如果有需要的話我放個連結:自定義表情QQ微信等表情包。

3:為了簡單起見我只選擇了7張圖片分別定義為f001-f007為圖片名,放到不同的解析度的資料夾下面,這步相信大家都會。

4:在values資料夾下面的arrays.xml中定義如下格式(當然根據我們自己的定義,這個定義是和文字一起傳輸代替表情的符號):

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <!--表情的自定義格式-->
    <string-array name="default_smiley_texts">
        <item>"[emoticon1]"</item>
        <item>"[emoticon2]"</item>
        <item>"[emoticon3]"</item>
        <item>"[emoticon4]"</item>
        <item>"[emoticon5]"</item>
        <item>"[emoticon6]"</item>
        <item>"[emoticon7]"</item>
    </string-array>
</resources>

5:構建一個工具類來解析我們的表情包,這裡我取名為SmileyParser.java檔案放在我們的工具資料夾下面如下:

     

package com.im.rainbowchat.logic.chat_friend.utils;

import android.content.Context;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ImageSpan;

import com.im.R;

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 表情解析工具類
 * 解析Text中的表情圖片
 * Created by AndyYuan on 2018/10/27.
 */

public class SmileyParser {
    /**
     * 單例模式 1文字資源,圖片資源 2.使用正則表示式進行匹配文字 3.把edittext當中整體的內容匹配正則表示式一次
     * 4.SpannableStringBuilder 進行替換
     */
    private static SmileyParser sInstance;

    public static SmileyParser getInstance() {
        return sInstance;
    }

    public static void init(Context context) {
        sInstance = new SmileyParser(context);
    }

    private final Context mContext;
    private final String[] arrText;
    // 正則表示式
    private final Pattern mPattern;
    // String 圖片字串 Integer表情
    private final HashMap<String, Integer> mSmileyToRes;
    // arrays裡面的表情內容
    public static final int DEFAULT_SMILEY_TEXTS = R.array.default_smiley_texts;

    private SmileyParser(Context context) {
        mContext = context;
        // 獲取表情文字資源
        arrText = mContext.getResources().getStringArray(DEFAULT_SMILEY_TEXTS);
        // 獲取表情ID與表情圖示的Map
        mSmileyToRes = buildSmileyToRes();
        // 獲取構建的正則表示式
        mPattern = buildPattern();
    }

    // 表情圖片集合
    private static final int[] DEFAULT_SMILEY_RES_IDS = {
            R.drawable.f001, R.drawable.f002,
            R.drawable.f003, R.drawable.f004, R.drawable.f005,
            R.drawable.f006, R.drawable.f007};

    /**
     * 使用HashMap的key-value的形式來影射表情的ID和圖片資源
     *
     * @return
     */
    private HashMap<String, Integer> buildSmileyToRes() {
        if (DEFAULT_SMILEY_RES_IDS.length != arrText.length) {
            throw new IllegalStateException("ID和圖片不匹配");
        }
        HashMap<String, Integer> smileyToRes = new HashMap<String, Integer>(
                arrText.length);
        for (int i = 0; i < arrText.length; i++) {
            // 圖片名稱作為key值,圖片資源ID作為value值
            smileyToRes.put(arrText[i], DEFAULT_SMILEY_RES_IDS[i]);
        }
        return smileyToRes;
    }

    /**
     * 構建正則表示式,用來找到我們所要使用的圖片
     *
     * @return
     */
    private Pattern buildPattern() {
        StringBuilder patternString = new StringBuilder(arrText.length * 3);
        patternString.append('(');
        for (String s : arrText) {
            patternString.append(Pattern.quote(s));
            patternString.append('|');
        }
        patternString.replace(patternString.length() - 1,
                patternString.length(), ")");
        // 把String字串編譯成正則表示式()
        // ([調皮]|[調皮]|[調皮])
        return Pattern.compile(patternString.toString());
    }

    /**
     * 根據文字替換成圖片
     *
     * @param text 對應表情
     * @return 一個表示圖片的序列
     */
    public CharSequence addSmileySpans(CharSequence text) {
        // 把文字替換為對應圖片
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        // 判斷提取工具類(按照正則表示式)
        Matcher matcher = mPattern.matcher(text);
        while (matcher.find()) {
            // 獲取對應表情的圖片id
            int resId = mSmileyToRes.get(matcher.group());
            // 替換制定字元
            builder.setSpan(new ImageSpan(mContext, resId), matcher.start(),
                    matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        return builder;
    }

}

6.基本工作就做到這裡,下面開始我們的關鍵的兩步:

   (一):顯示的地方,我們在自定義的輸入框(就是上面的傳送文字表情的自定義的輸入框)下面留一定的佔位空間其中XML檔案如下:

        

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/rl_bottom"
        android:layout_width="fill_parent"
        android:layout_height="50dp"
        android:background="#fff"
        android:gravity="center">

        <Button
            android:id="@+id/multi_chatting_list_view_sendVoiceBtn"
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:layout_marginLeft="5dp"
            android:background="@drawable/chat_realvoice_btn"
            android:text="" />


        <com.im.rainbowchat.logic.chat_friend.EmotionsEditText
            android:id="@+id/multi_chatting_list_view_msgEdit"
            android:layout_width="0dip"
            android:layout_height="33dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:layout_weight="1"
            android:background="@drawable/chat_put_in"
            android:focusable="true"
            android:hint="@string/chat_message_input_hint"
            android:imeOptions="actionSearch"
            android:inputType="text"
            android:paddingLeft="7dp"
            android:singleLine="true"
            android:textColor="@color/black"
            android:textSize="16sp" />

        <Button
            android:id="@+id/multi_chatting_list_view_sendImgBtn"
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:background="@drawable/chat_plus_emoji"
            android:text="" />


        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center">

            <Button
                android:id="@+id/multi_chatting_list_view_plusBtn"
                android:layout_width="35dp"
                android:layout_height="35dp"
                android:layout_marginLeft="0dp"
                android:layout_marginRight="5dp"
                android:background="@drawable/chat_plus_btn"
                android:text="" />

            <Button
                android:id="@+id/multi_chatting_list_view_sendBtn"
                android:layout_width="38dp"
                android:layout_height="33dp"
                android:layout_marginLeft="0dp"
                android:layout_marginRight="5dp"
                android:background="@drawable/chat_send_btn"
                android:text="@string/chat_send_message"
                android:textColor="@color/white"
                android:textSize="13sp"
                android:visibility="gone" />

            <TextView
                android:id="@+id/multi_chatting_list_view_prohibitText"
                style="@style/text_witheShadow2"
                android:layout_width="38dp"
                android:layout_height="35dp"
                android:layout_marginRight="5dp"
                android:background="@drawable/bbs_chatting_prohibit_speech_checker_bgimg"
                android:clickable="true"
                android:gravity="center"
                android:text="--"
                android:textColor="@color/red_for_text"
                android:textSize="18sp"
                android:visibility="gone" />

        </FrameLayout>
    </LinearLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
//這個就是表情包的佔位空間
        <FrameLayout
            android:id="@+id/multi_chatting_recycler_view_bottomContentFL"
            android:layout_width="fill_parent"
            android:layout_height="190dp"
            android:background="@drawable/chat_plus_functions_layout_bg"
            android:gravity="center"
            android:orientation="vertical"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="15dp"
            android:visibility="gone"></FrameLayout>
    </FrameLayout>

</LinearLayout>

(二):在程式碼中初始化我們的表情

  

abstract class GroupChattingEmojiUIWrapper {

    protected Activity parentActivity = null;
    /**
     * 主Activity的layout預留給本佈局的父佈局
     */
    protected FrameLayout layoutbottomContentOfParent = null;

    private List<EmojiEntity> emojiEntityList = new ArrayList<>();

    /**
     * recyclerView 佈局物件
     */
    private RecyclerView emojiRecyclerView = null;


    public GroupChattingEmojiUIWrapper(final Activity context
            , final FrameLayout layoutbottomContent) {

        this.parentActivity = context;
        this.layoutbottomContentOfParent = layoutbottomContent;

        EmojiUtil.initEmojis(emojiEntityList);
        this.emojiRecyclerView = new RecyclerView(context);

        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(8, StaggeredGridLayoutManager.VERTICAL);
        emojiRecyclerView.setLayoutManager(layoutManager);
        EmojiAdapter emojiAdapter = new EmojiAdapter(emojiEntityList);
        emojiRecyclerView.setAdapter(emojiAdapter);

        // 將更多功能的recyclerview加入到輸入框下方的更多功能父佈局中
        this.layoutbottomContentOfParent.addView(this.emojiRecyclerView, new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
    }


    public void auto() {
        if (this.isShowing()) {
            this.hide();
        } else {
            this.show();
        }
    }

    public void show() {
        this.layoutbottomContentOfParent.setVisibility(View.VISIBLE);
    }

    public void hide() {
        this.layoutbottomContentOfParent.setVisibility(View.GONE);
    }

    public boolean isShowing() {
        return (this.layoutbottomContentOfParent.getVisibility() == View.VISIBLE);
    }

    class EmojiAdapter extends RecyclerView.Adapter<EmojiAdapter.ViewHolder> {

        private List<EmojiEntity> mEmojiEntity;

        class ViewHolder extends RecyclerView.ViewHolder {
            ImageView emojiIV;

            public ViewHolder(View view) {
                super(view);
                emojiIV = view.findViewById(R.id.chatting_plus_functions_gridview_item_imageView);
            }
        }

        public EmojiAdapter(List<EmojiEntity> emojiEntities) {
            mEmojiEntity = emojiEntities;
        }

        @Override
        public EmojiAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chatting_plus_emoji_gridview_item, parent, false);
            EmojiAdapter.ViewHolder holder = new EmojiAdapter.ViewHolder(view);
            return holder;
        }

        @Override
        public void onBindViewHolder(EmojiAdapter.ViewHolder holder, final int position) {
            final EmojiEntity emojiEntity = mEmojiEntity.get(position);
            holder.emojiIV.setImageResource(emojiEntity.getEmojiID());
            holder.emojiIV.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //txtMsg.getText().append("hhh");
                    try {
                        //獲取表情圖片檔名
                        int resourceId = emojiEntity.getEmojiID();
                        // 在android中要顯示圖片資訊,必須使用Bitmap點陣圖的物件來裝載
                        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resourceId);
                        //要讓圖片替代指定的文字就要用ImageSpan
                        ImageSpan imageSpan = new ImageSpan(AlarmsActivity.this, bitmap);
                        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(emojiEntity.getName());//圖片的字首名
                        spannableStringBuilder.setSpan(imageSpan, 0, 11, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                          //在此地方做一個判斷,如果游標是在最後就直接append新增到最後就可以了
                            if(txtMsg.getSelectionStart()==txtMsg.getText().length()){
                                txtMsg.append(spannableStringBuilder);
                            }else{
                                //如果游標在中間就把這個富文字新增到字型行間,從而解決不能在文字中插入表情的問題
                                int index=txtMsg.getSelectionStart();
                                Editable editable=txtMsg.getText();
                                editable.insert(index,spannableStringBuilder);
                            }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        @Override
        public int getItemCount() {
            return mEmojiEntity.size();
        }

    }
}

7:最後的收到訊息並解析出來:

SmileyParser.init(context);
SmileyParser smileyParser = SmileyParser.getInstance();
((TextView) viewHolder.tvContent).setText(smileyParser.addSmileySpans(msgContent));

好了,不知不覺中就完成了表情包的傳送功能,其實查閱資料得知QQ微信等都是使用這種方式傳送的,如果有不明白的請留言,我會繼續更新的。