安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情

前言;正常实现聊天功能想必大家都使用三方的Sdk比如环信融云集成的,但是聊天记录的保存只能有三天,想增加保存时长就需要花钱,so 我只好自己想办法实现了,这个demo是类似于留言板,并非即时通讯!只实现了表情文字图文混排,可以通过手动刷新实现即时通讯ok废话少说,先看效果图;

安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第1张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第2张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第3张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第4张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第5张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第6张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第7张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第8张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第9张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第10张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第11张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第12张图片
安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第13张图片

项目下载地址
https://github.com/PangHaHa12138/TestChatdemo
表情下载地址
http://emojipedia.org/apple/
大体思路:
1.先从布局开始,聊天界面就是多条目的listview,左右各算一种类型,这样正常语音,文字,图文混排,大图片,大表情,视频,都是x2倍的,聊天条目就是listview条目背景透明,然后imageview+给textview设置气泡的聊天背景
不过我现在暂时只实现了文字表情,然后键盘这块是gridview实现,写正则来过滤表情的编码,点击事件判断选中表情还是删除
感谢开源键盘控件:
https://github.com/w446108264/XhsEmoticonsKeyboard
继承XhsEmoticonsKeyboard控件写一个带表情的键盘
2.代码大体逻辑,发送的消息其实就是一个文本内容,通过SpannableStringBuilder和Spannable实现图文混排,其实就是把各种表情序列号也可以说是索引写到一个xml文件里,然后用一个map来存这些图片对应的编码,其实就是图片名字,然后通过正则来找到正确的索引,即xml里存的图片对应文件夹drawable下的

SpannableStringBuilder spannableStringBuilder = new 
SpannableStringBuilder(content);Spannable spannable = 
QqFilter.spannableFilter(tv_content.getContext(), spannableStringBuilder, content, 
EmoticonsKeyboardUtils.getFontHeight(tv_content), null);tv_content.setText(spannable);

然后就是listview的展示了,页面初始化的时候根据服务器返回的字段判断是别人发的在左边,还是我发的在右面,然后在adapter里调用图文混排的方法找到对应表情图片填充条目,发送的时候先检查是否是正确的表情字符,找到对应表情图片的集合map,找到对应的表情名字,然后和文字一起传到服务器,然后进行网络请求,上传成功之后再刷新界面,上拉加载的话是加载历史记录全部的,下拉刷新是请求最新的,每次请求都控制页面显示最多28条数据,也就是4页,上来初始化页面也是,然后可以通过聊完上拉不停的重复手动实现即时通讯


安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情_第14张图片

当然这是开玩笑了
下面上代码:
主要布局



    

    
        
        
        
        
        
    

    


    

主界面,下面四个按钮,切换四个fragment
聊天界面布局 最外层用自己继承
XhsEmoticonsKeyBoard 键盘类




    

        
        

            
            


        

        


            
        

    


键盘类

public class SimpleUserdefEmoticonsKeyBoard extends XhsEmoticonsKeyBoard {

    public final int APPS_HEIGHT = 120;

    public SimpleUserdefEmoticonsKeyBoard(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void inflateKeyboardBar(){
        mInflater.inflate(R.layout.view_keyboard_userdef, this);
    }

    @Override
    protected View inflateFunc(){
        return mInflater.inflate(R.layout.view_func_emoticon_userdef, null);
    }

    @Override
    public void reset() {
        EmoticonsKeyboardUtils.closeSoftKeyboard(getContext());
        mLyKvml.hideAllFuncView();
        mBtnFace.setImageResource(R.drawable.chatting_emoticons);
    }

    @Override
    public void onFuncChange(int key) {
        if (FUNC_TYPE_EMOTION == key) {
            mBtnFace.setImageResource(R.drawable.chatting_softkeyboard);
        } else {
            mBtnFace.setImageResource(R.drawable.chatting_emoticons);
        }
        checkVoice();
    }

    @Override
    public void OnSoftClose() {
        super.OnSoftClose();
        if (mLyKvml.getCurrentFuncKey() == FUNC_TYPE_APPPS) {
            setFuncViewHeight(EmoticonsKeyboardUtils.dip2px(getContext(), APPS_HEIGHT));
        }
    }

    @Override
    protected void showText() {
        mEtChat.setVisibility(VISIBLE);
        mBtnFace.setVisibility(VISIBLE);
        mBtnVoice.setVisibility(GONE);
    }

    @Override
    protected void showVoice() {
        mEtChat.setVisibility(GONE);
        mBtnFace.setVisibility(GONE);
        mBtnVoice.setVisibility(VISIBLE);
        reset();
    }

    @Override
    protected void checkVoice() {
        if (mBtnVoice.isShown()) {
            mBtnVoiceOrText.setImageResource(R.drawable.chatting_softkeyboard);
        } else {
            mBtnVoiceOrText.setImageResource(R.drawable.chatting_vodie);
        }
    }

    @Override
    public void onClick(View v) {
        int i = v.getId();
        if (i == com.keyboard.view.R.id.btn_voice_or_text) {
            if (mEtChat.isShown()) {
                mBtnVoiceOrText.setImageResource(R.drawable.chatting_softkeyboard);
                showVoice();
            } else {
                showText();
                mBtnVoiceOrText.setImageResource(R.drawable.chatting_vodie);
                EmoticonsKeyboardUtils.openSoftKeyboard(mEtChat);
            }
        } else if (i == com.keyboard.view.R.id.btn_face) {
            toggleFuncView(FUNC_TYPE_EMOTION);
        } else if (i == com.keyboard.view.R.id.btn_multimedia) {
            toggleFuncView(FUNC_TYPE_APPPS);
            setFuncViewHeight(EmoticonsKeyboardUtils.dip2px(getContext(), APPS_HEIGHT));
        }
    }

表情过滤和定位类

public class QqFilter extends EmoticonFilter {

    public static final int WRAP_DRAWABLE = -1;
    private int emoticonSize = -1;
    public static final Pattern QQ_RANGE = Pattern.compile("\\[[a-zA-Z0-9\\u4e00-\\u9fa5]+\\]");

    public static Matcher getMatcher(CharSequence matchStr) {
        return QQ_RANGE.matcher(matchStr);
    }

    @Override
    public void filter(EditText editText, CharSequence text, int start, int lengthBefore, int lengthAfter) {
        emoticonSize = emoticonSize == -1 ? EmoticonsKeyboardUtils.getFontHeight(editText) : emoticonSize;
        clearSpan(editText.getText(), start, text.toString().length());
        Matcher m = getMatcher(text.toString().substring(start, text.toString().length()));
        if (m != null) {
            while (m.find()) {
                String key = m.group();
                int icon = DefQqEmoticons.sQqEmoticonHashMap.get(key);
                if (icon > 0) {
                    emoticonDisplay(editText.getContext(), editText.getText(), icon, emoticonSize, start + m.start(), start + m.end());
                }
            }
        }
    }

    public static Spannable spannableFilter(Context context, Spannable spannable, CharSequence text, int fontSize, EmojiDisplayListener emojiDisplayListener) {
        Matcher m = getMatcher(text);
        if (m != null) {
            while (m.find()) {
                String key = m.group();
                int icon = DefQqEmoticons.sQqEmoticonHashMap.get(key);
                if (emojiDisplayListener == null) {
                    if (icon > 0) {
                        emoticonDisplay(context, spannable, icon, fontSize, m.start(), m.end());
                    }
                } else {
                    emojiDisplayListener.onEmojiDisplay(context, spannable, "" + icon, fontSize, m.start(), m.end());
                }
            }
        }
        return spannable;
    }

    private void clearSpan(Spannable spannable, int start, int end) {
        if (start == end) {
            return;
        }
        EmoticonSpan[] oldSpans = spannable.getSpans(start, end, EmoticonSpan.class);
        for (int i = 0; i < oldSpans.length; i++) {
            spannable.removeSpan(oldSpans[i]);
        }
    }

    public static void emoticonDisplay(Context context, Spannable spannable, int emoticon, int fontSize, int start, int end) {
        Drawable drawable = getDrawable(context, emoticon);
        if (drawable != null) {
            int itemHeight;
            int itemWidth;
            if (fontSize == WRAP_DRAWABLE) {
                itemHeight = drawable.getIntrinsicHeight();
                itemWidth = drawable.getIntrinsicWidth();
            } else {
                itemHeight = fontSize;
                itemWidth = fontSize;
            }
            drawable.setBounds(0, 0, itemHeight, itemWidth);
            EmoticonSpan imageSpan = new EmoticonSpan(drawable);
            spannable.setSpan(imageSpan, start, end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
        }
    }

生成添加各类型表情的工厂

package com.panghaha.it.testchatdemo.common;

import android.content.Context;
import android.text.Editable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;

import com.panghaha.it.testchatdemo.R;
import com.sj.emoji.DefEmoticons;
import com.sj.emoji.EmojiBean;
import com.testemticon.DefXhsEmoticons;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;

import sj.keyboard.adpater.EmoticonsAdapter;
import sj.keyboard.adpater.PageSetAdapter;
import sj.keyboard.data.EmoticonEntity;
import sj.keyboard.data.EmoticonPageEntity;
import sj.keyboard.data.EmoticonPageSetEntity;
import sj.keyboard.data.PageEntity;
import sj.keyboard.data.PageSetEntity;
import sj.keyboard.interfaces.EmoticonClickListener;
import sj.keyboard.interfaces.EmoticonDisplayListener;
import sj.keyboard.interfaces.PageViewInstantiateListener;
import sj.keyboard.utils.EmoticonsKeyboardUtils;
import sj.keyboard.utils.imageloader.ImageBase;
import sj.keyboard.utils.imageloader.ImageLoader;
import sj.keyboard.widget.EmoticonPageView;
import sj.keyboard.widget.EmoticonsEditText;
import sj.qqkeyboard.DefQqEmoticons;

/**
 *
 * 表情工厂类 加载表情种类
 * */

public class SimpleCommonUtils {

    public static void initEmoticonsEditText(EmoticonsEditText etContent) {
        etContent.addEmoticonFilter(new EmojiFilter());
        etContent.addEmoticonFilter(new XhsFilter());
    }

    public static EmoticonClickListener getCommonEmoticonClickListener(final EditText editText) {
        return new EmoticonClickListener() {
            @Override
            public void onEmoticonClick(Object o, int actionType, boolean isDelBtn) {
                if (isDelBtn) {
                    SimpleCommonUtils.delClick(editText);
                } else {
                    if (o == null) {
                        return;
                    }
                    if (actionType == Constants.EMOTICON_CLICK_TEXT) {
                        String content = null;
                        if (o instanceof EmojiBean) {
                            content = ((EmojiBean) o).emoji;
                        } else if (o instanceof EmoticonEntity) {
                            content = ((EmoticonEntity) o).getContent();
                        }

                        if (TextUtils.isEmpty(content)) {
                            return;
                        }
                        int index = editText.getSelectionStart();
                        Editable editable = editText.getText();
                        editable.insert(index, content);
                    }
                }
            }
        };
    }

    public static PageSetAdapter sCommonPageSetAdapter;

    public static PageSetAdapter getCommonAdapter(Context context, EmoticonClickListener emoticonClickListener) {

        if(sCommonPageSetAdapter != null){
            return sCommonPageSetAdapter;
        }

        PageSetAdapter pageSetAdapter = new PageSetAdapter();
        //原生的笑脸表情
//        addEmojiPageSetEntity(pageSetAdapter, context, emoticonClickListener);
        //QQ笑脸表情
        addQqPageSetEntity(pageSetAdapter, context, emoticonClickListener);

        //龟头表情
//        addXhsPageSetEntity(pageSetAdapter, context, emoticonClickListener);
        //兔斯基
//        addWechatPageSetEntity(pageSetAdapter, context, emoticonClickListener);
        //好好学习表情包
//        addGoodGoodStudyPageSetEntity(pageSetAdapter, context, emoticonClickListener);
        //颜文字
        addKaomojiPageSetEntity(pageSetAdapter, context, emoticonClickListener);

//        addTestPageSetEntity(pageSetAdapter, context);

        return pageSetAdapter;
    }

    /**
     * 插入emoji表情集
     *
     * @param pageSetAdapter
     * @param context
     * @param emoticonClickListener
     */
    public static void addEmojiPageSetEntity(PageSetAdapter pageSetAdapter, Context context, final EmoticonClickListener emoticonClickListener) {
        ArrayList emojiArray = new ArrayList<>();
        Collections.addAll(emojiArray, DefEmoticons.sEmojiArray);
        EmoticonPageSetEntity emojiPageSetEntity
                = new EmoticonPageSetEntity.Builder()
                .setLine(3)
                .setRow(7)
                .setEmoticonList(emojiArray)
                .setIPageViewInstantiateItem(getDefaultEmoticonPageViewInstantiateItem(new EmoticonDisplayListener() {
                    @Override
                    public void onBindView(int position, ViewGroup parent, EmoticonsAdapter.ViewHolder viewHolder, Object object, final boolean isDelBtn) {
                        final EmojiBean emojiBean = (EmojiBean) object;
                        if (emojiBean == null && !isDelBtn) {
                            return;
                        }

                        viewHolder.ly_root.setBackgroundResource(com.keyboard.view.R.drawable.bg_emoticon);

                        if (isDelBtn) {
                            viewHolder.iv_emoticon.setImageResource(R.drawable.icon_del);
                        } else {
                            viewHolder.iv_emoticon.setImageResource(emojiBean.icon);
                        }

                        viewHolder.rootView.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                if (emoticonClickListener != null) {
                                    emoticonClickListener.onEmoticonClick(emojiBean, Constants.EMOTICON_CLICK_TEXT, isDelBtn);
                                }
                            }
                        });
                    }
                }))
                .setShowDelBtn(EmoticonPageEntity.DelBtnStatus.LAST)
                .setIconUri(ImageBase.Scheme.DRAWABLE.toUri("icon_emoji"))
                .build();
        pageSetAdapter.add(emojiPageSetEntity);
    }


    public static void addQqPageSetEntity(PageSetAdapter pageSetAdapter, Context context, final EmoticonClickListener emoticonClickListener) {
        EmoticonPageSetEntity kaomojiPageSetEntity
                = new EmoticonPageSetEntity.Builder()
                .setLine(3)
                .setRow(7)
                .setEmoticonList(ParseDataUtils.ParseQqData(DefQqEmoticons.sQqEmoticonHashMap))
                .setIPageViewInstantiateItem(new PageViewInstantiateListener() {
                    @Override
                    public View instantiateItem(ViewGroup container, int position, EmoticonPageEntity pageEntity) {
                        if (pageEntity.getRootView() == null) {
                            EmoticonPageView pageView = new EmoticonPageView(container.getContext());
                            pageView.setNumColumns(pageEntity.getRow());
                            pageEntity.setRootView(pageView);
                            try {
                                EmoticonsAdapter adapter = new EmoticonsAdapter(container.getContext(), pageEntity, emoticonClickListener);
                                adapter.setItemHeightMaxRatio(1.8);
                                adapter.setOnDisPlayListener(getEmoticonDisplayListener(emoticonClickListener));
                                pageView.getEmoticonsGridView().setAdapter(adapter);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        return pageEntity.getRootView();
                    }
                })
                .setShowDelBtn(EmoticonPageEntity.DelBtnStatus.LAST)
                .setIconUri(ImageBase.Scheme.DRAWABLE.toUri("kys"))
                .build();
        pageSetAdapter.add(kaomojiPageSetEntity);
    }



    public static EmoticonDisplayListener getEmoticonDisplayListener(final EmoticonClickListener emoticonClickListener){
        return new EmoticonDisplayListener() {
            @Override
            public void onBindView(int position, ViewGroup parent, EmoticonsAdapter.ViewHolder viewHolder, Object object, final boolean isDelBtn) {
                final EmoticonEntity emoticonEntity = (EmoticonEntity) object;
                if (emoticonEntity == null && !isDelBtn) {
                    return;
                }
                viewHolder.ly_root.setBackgroundResource(com.keyboard.view.R.drawable.bg_emoticon);

                if (isDelBtn) {
                    viewHolder.iv_emoticon.setImageResource(R.drawable.icon_del);
                } else {
                    try {
                        ImageLoader.getInstance(viewHolder.iv_emoticon.getContext()).displayImage(emoticonEntity.getIconUri(), viewHolder.iv_emoticon);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                viewHolder.rootView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (emoticonClickListener != null) {
                            emoticonClickListener.onEmoticonClick(emoticonEntity, Constants.EMOTICON_CLICK_TEXT, isDelBtn);
                        }
                    }
                });
            }
        };
    }


    /**
     * 插入xhs表情集
     *
     * @param pageSetAdapter
     * @param context
     * @param emoticonClickListener
     */
    public static void addXhsPageSetEntity(PageSetAdapter pageSetAdapter, Context context, EmoticonClickListener emoticonClickListener) {
        EmoticonPageSetEntity xhsPageSetEntity
                = new EmoticonPageSetEntity.Builder()
                .setLine(3)
                .setRow(7)
                .setEmoticonList(ParseDataUtils.ParseXhsData(DefXhsEmoticons.xhsEmoticonArray, ImageBase.Scheme.ASSETS))
                .setIPageViewInstantiateItem(getDefaultEmoticonPageViewInstantiateItem(getCommonEmoticonDisplayListener(emoticonClickListener, Constants.EMOTICON_CLICK_TEXT)))
                .setShowDelBtn(EmoticonPageEntity.DelBtnStatus.LAST)
                .setIconUri(ImageBase.Scheme.ASSETS.toUri("xhsemoji_19.png"))
                .build();
        pageSetAdapter.add(xhsPageSetEntity);
    }

    /**
     * 插入微信表情集
     *
     * @param pageSetAdapter
     * @param context
     * @param emoticonClickListener
     */
    public static void addWechatPageSetEntity(PageSetAdapter pageSetAdapter, Context context, EmoticonClickListener emoticonClickListener) {
        String filePath = FileUtils.getFolderPath("wxemoticons");
        EmoticonPageSetEntity emoticonPageSetEntity = ParseDataUtils.parseDataFromFile(context, filePath, "wxemoticons.zip", "wxemoticons.xml");
        if (emoticonPageSetEntity == null) {
            return;
        }
        EmoticonPageSetEntity pageSetEntity
                = new EmoticonPageSetEntity.Builder()
                .setLine(emoticonPageSetEntity.getLine())
                .setRow(emoticonPageSetEntity.getRow())
                .setEmoticonList(emoticonPageSetEntity.getEmoticonList())
                .setIPageViewInstantiateItem(getEmoticonPageViewInstantiateItem(BigEmoticonsAdapter.class, emoticonClickListener))
                .setIconUri(ImageBase.Scheme.FILE.toUri(filePath + "/" + emoticonPageSetEntity.getIconUri()))
                .build();
        pageSetAdapter.add(pageSetEntity);
    }

    /**
     * 插入我们爱学习表情集
     *
     * @param pageSetAdapter
     * @param context
     * @param emoticonClickListener
     */
    public static void addGoodGoodStudyPageSetEntity(PageSetAdapter pageSetAdapter, Context context, EmoticonClickListener emoticonClickListener) {
        String filePath = FileUtils.getFolderPath("goodgoodstudy");
        EmoticonPageSetEntity emoticonPageSetEntity = ParseDataUtils.parseDataFromFile(context, filePath, "goodgoodstudy.zip", "goodgoodstudy.xml");
        if (emoticonPageSetEntity == null) {
            return;
        }
        EmoticonPageSetEntity pageSetEntity
                = new EmoticonPageSetEntity.Builder()
                .setLine(emoticonPageSetEntity.getLine())
                .setRow(emoticonPageSetEntity.getRow())
                .setEmoticonList(emoticonPageSetEntity.getEmoticonList())
                .setIPageViewInstantiateItem(getEmoticonPageViewInstantiateItem(BigEmoticonsAndTitleAdapter.class, emoticonClickListener))
                .setIconUri(ImageBase.Scheme.FILE.toUri(filePath + "/" + emoticonPageSetEntity.getIconUri()))
                .build();
        pageSetAdapter.add(pageSetEntity);
    }


    /**
     * 插入颜文字表情集
     *
     * @param pageSetAdapter
     * @param context
     * @param emoticonClickListener
     */
    public static void addKaomojiPageSetEntity(PageSetAdapter pageSetAdapter, Context context, EmoticonClickListener emoticonClickListener) {
        EmoticonPageSetEntity kaomojiPageSetEntity
                = new EmoticonPageSetEntity.Builder()
                .setLine(3)
                .setRow(3)
                .setEmoticonList(ParseDataUtils.parseKaomojiData(context))
                .setIPageViewInstantiateItem(getEmoticonPageViewInstantiateItem(TextEmoticonsAdapter.class, emoticonClickListener))
                .setIconUri(ImageBase.Scheme.DRAWABLE.toUri("icon_kaomoji"))
                .build();
        pageSetAdapter.add(kaomojiPageSetEntity);
    }

    /**
     * 测试页集
     *
     * @param pageSetAdapter
     * @param context
     */
    public static void addTestPageSetEntity(PageSetAdapter pageSetAdapter, Context context) {
        PageSetEntity pageSetEntity = new PageSetEntity.Builder()
                .addPageEntity(new PageEntity(new SimpleAppsGridView(context)))
                .setIconUri(ImageBase.Scheme.DRAWABLE.toUri("icon_kaomoji"))
                .setShowIndicator(false)
                .build();
        pageSetAdapter.add(pageSetEntity);
    }

    @SuppressWarnings("unchecked")
    public static Object newInstance(Class _Class, Object... args) throws Exception {
        return newInstance(_Class, 0, args);
    }

    @SuppressWarnings("unchecked")
    public static Object newInstance(Class _Class, int constructorIndex, Object... args) throws Exception {
        Constructor cons = _Class.getConstructors()[constructorIndex];
        return cons.newInstance(args);
    }

    public static PageViewInstantiateListener getDefaultEmoticonPageViewInstantiateItem(final EmoticonDisplayListener emoticonDisplayListener) {
        return getEmoticonPageViewInstantiateItem(EmoticonsAdapter.class, null, emoticonDisplayListener);
    }

    public static PageViewInstantiateListener getEmoticonPageViewInstantiateItem(final Class _class, EmoticonClickListener onEmoticonClickListener) {
        return getEmoticonPageViewInstantiateItem(_class, onEmoticonClickListener, null);
    }

    public static PageViewInstantiateListener getEmoticonPageViewInstantiateItem(final Class _class, final EmoticonClickListener onEmoticonClickListener, final EmoticonDisplayListener emoticonDisplayListener) {
        return new PageViewInstantiateListener() {
            @Override
            public View instantiateItem(ViewGroup container, int position, EmoticonPageEntity pageEntity) {
                if (pageEntity.getRootView() == null) {
                    EmoticonPageView pageView = new EmoticonPageView(container.getContext());
                    pageView.setNumColumns(pageEntity.getRow());
                    pageEntity.setRootView(pageView);
                    try {
                        EmoticonsAdapter adapter = (EmoticonsAdapter) newInstance(_class, container.getContext(), pageEntity, onEmoticonClickListener);
                        if (emoticonDisplayListener != null) {
                            adapter.setOnDisPlayListener(emoticonDisplayListener);
                        }
                        pageView.getEmoticonsGridView().setAdapter(adapter);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                return pageEntity.getRootView();
            }
        };
    }

    public static EmoticonDisplayListener getCommonEmoticonDisplayListener(final EmoticonClickListener onEmoticonClickListener, final int type) {
        return new EmoticonDisplayListener() {
            @Override
            public void onBindView(int position, ViewGroup parent, EmoticonsAdapter.ViewHolder viewHolder, Object object, final boolean isDelBtn) {

                final EmoticonEntity emoticonEntity = (EmoticonEntity) object;
                if (emoticonEntity == null && !isDelBtn) {
                    return;
                }
                viewHolder.ly_root.setBackgroundResource(com.keyboard.view.R.drawable.bg_emoticon);

                if (isDelBtn) {
                    viewHolder.iv_emoticon.setImageResource(R.drawable.icon_del);
                } else {
                    try {
                        ImageLoader.getInstance(viewHolder.iv_emoticon.getContext()).displayImage(emoticonEntity.getIconUri(), viewHolder.iv_emoticon);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                viewHolder.rootView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (onEmoticonClickListener != null) {
                            onEmoticonClickListener.onEmoticonClick(emoticonEntity, type, isDelBtn);
                        }
                    }
                });
            }
        };
    }

    public static void delClick(EditText editText) {
        int action = KeyEvent.ACTION_DOWN;
        int code = KeyEvent.KEYCODE_DEL;
        KeyEvent event = new KeyEvent(action, code);
        editText.onKeyDown(KeyEvent.KEYCODE_DEL, event);
    }

    public static void spannableEmoticonFilter(TextView tv_content, String content) {
        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(content);

        Spannable spannable = QqFilter.spannableFilter(tv_content.getContext(),
                spannableStringBuilder,
                content,
                EmoticonsKeyboardUtils.getFontHeight(tv_content),
                null);
        tv_content.setText(spannable);

        /* Spannable spannable = EmojiDisplay.spannableFilter(tv_content.getContext(),
                spannableStringBuilder,
                content,
                EmoticonsKeyboardUtils.getFontHeight(tv_content));*/

//        spannable = XhsFilter.spannableFilter(tv_content.getContext(),
//                spannable,
//                content,
//                EmoticonsKeyboardUtils.getFontHeight(tv_content),
//                null);
//        tv_content.setText(spannable);
    }
}
 
 

listview adapter

package com.panghaha.it.testchatdemo.common;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.panghaha.it.testchatdemo.R;

import java.util.List;


public class NyChattingListAdapter extends BaseAdapter {

    private final int VIEW_TYPE_COUNT = 8;
    private final int VIEW_TYPE_LEFT_TEXT = 0;
    private final int VIEW_TYPE_LEFT_IMAGE = 1;
    private final int VIEW_TYPE_RIGTH_TEXT = 2;
    private final int VIEW_TYPE_RIGTH_IMAGE = 3;

    private Activity mActivity;
    private LayoutInflater mInflater;
    private List mData;
    private String myuserid;
    public int isMesend;
    public int istext;
    public NyChattingListAdapter(Activity activity,String id,List list) {
        this.mActivity = activity;
        this.myuserid = id;
        this.mData = list;
        mInflater = LayoutInflater.from(activity);
    }

    public void setsendtype(int i){
        isMesend = i;
    }

    @Override
    public int getCount() {
        return mData == null ? 0 : mData.size();
    }

    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemViewType(int position) {
        if (mData.get(position) == null) {
            return -1;
        }
        if (mData.get(position).getUserid()!=null&&mData.get(position).getUserid().equals(myuserid)||isMesend == 1){

            return VIEW_TYPE_RIGTH_TEXT;
        }else if (mData.get(position).getUserid()!=null&&!mData.get(position).getUserid().equals(myuserid)){

            return VIEW_TYPE_LEFT_TEXT;
        }
        return -1;
    }

    @Override
    public int getViewTypeCount() {
        return VIEW_TYPE_COUNT;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final Data_ReceiverNews.NewsBean bean = mData.get(position);
        int type = getItemViewType(position);
        ViewHolderText rightholder;
        if (convertView == null) {
            if (type == VIEW_TYPE_RIGTH_TEXT ){
                convertView = mInflater.inflate(R.layout.listitem_cha_right_text, null);//接收的消息
            }else if (type == VIEW_TYPE_LEFT_TEXT){
                convertView = mInflater.inflate(R.layout.listitem_cha_left_text, null);//发送的消息
            }
            rightholder = new ViewHolderText();

            rightholder.iv_avatar = (CircleImageView) convertView.findViewById(R.id.iv_avatar);
            rightholder.tv_content = (TextView) convertView.findViewById(R.id.tv_content);
            rightholder.sendtime = (TextView) convertView.findViewById(R.id.sendtime);
            rightholder.name = (TextView) convertView.findViewById(R.id.chatname);

            convertView.setTag(rightholder);
        } else {
            rightholder = (ViewHolderText) convertView.getTag();
        }

        if (type == VIEW_TYPE_RIGTH_TEXT){
            String time = bean.getCreatime();
            String tim = time.substring(0,time.length()-2);
            rightholder.sendtime.setText(tim);//设置头像和姓名注释了
//            rightholder.name.setText(SharedPreferencesUtil.readUsername(mActivity));
//            if (SharedPreferencesUtil.readAvatar(mActivity)!=null){
//                Glide.with(mActivity).load(SharedPreferencesUtil.readAvatar(mActivity)).into(rightholder.iv_avatar);
//            }
            disPlayRightTextView(position, convertView, rightholder, bean);
        }else if (type == VIEW_TYPE_LEFT_TEXT){
            String time = bean.getCreatime();
            String tim = time.substring(0,time.length()-2);
            rightholder.sendtime.setText(tim);//设置头像和姓名注释了
//            rightholder.name.setText(bean.getUserName());
//            if (bean.getPath()!=null){
//                Glide.with(mActivity).load(bean.getPath()).into(rightholder.iv_avatar);
//            }
            disPlayRightTextView(position, convertView, rightholder, bean);
        }



        return convertView;
    }
    //图文混排 设置消息
    public void disPlayRightTextView(int position, View view, ViewHolderText holder, Data_ReceiverNews.NewsBean bean) {
        setContent2(holder.tv_content, bean.getNewscontent());
    }
    //这里是添加表情
    public void setContent2(TextView tv_content, String content) {
        SimpleCommonUtils.spannableEmoticonFilter(tv_content, content);
    }

    public final class ViewHolderText {
        public CircleImageView iv_avatar;
        public TextView tv_content;
        public TextView sendtime;
        public TextView name;
    }
}

聊天主类

package com.panghaha.it.testchatdemo;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.lzy.okhttputils.OkHttpUtils;
import com.lzy.okhttputils.callback.StringCallback;
import com.panghaha.it.testchatdemo.common.ChattingListAdapter;
import com.panghaha.it.testchatdemo.common.Constants;
import com.panghaha.it.testchatdemo.common.Data_ReceiverNews;
import com.panghaha.it.testchatdemo.common.NyChattingListAdapter;
import com.panghaha.it.testchatdemo.common.SimpleCommonUtils;
import com.panghaha.it.testchatdemo.common.SimpleUserDefAppsGridView;
import com.panghaha.it.testchatdemo.common.SimpleUserdefEmoticonsKeyBoard;
import com.sj.emoji.EmojiBean;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import okhttp3.Call;
import okhttp3.Response;
import sj.keyboard.data.EmoticonEntity;
import sj.keyboard.interfaces.EmoticonClickListener;
import sj.keyboard.widget.EmoticonsEditText;
import sj.keyboard.widget.FuncLayout;

/***
 * ━━━━ Code is far away from ━━━━━━
 *     ()      ()
 *     ( )    ( )
 *     ( )    ( )
 *   ┏┛┻━━━┛┻┓
 *   ┃   ━   ┃
 *   ┃ ┳┛ ┗┳ ┃
 *   ┃   ┻   ┃
 *   ┗━┓   ┏━┛
 *     ┃   ┃
 *     ┃   ┗━━━┓
 *     ┃       ┣┓
 *     ┃       ┏┛
 *     ┗┓┓┏━┳┓┏┛
 *      ┃┫┫ ┃┫┫
 *      ┗┻┛ ┗┻┛
 * ━━━━ bug with the more protecting ━━━
 * 

* Created by PangHaHa12138 on 2017/6/7. */ public class ActivityChat extends AppCompatActivity implements FuncLayout.OnFuncKeyBoardListener { private ListView lvChat; private SimpleUserdefEmoticonsKeyBoard ekBar; private ChattingListAdapter chattingListAdapter; // private MyChattingAdapter myChattingAdapter; private NyChattingListAdapter myChattingAdapter; private Toolbar toolbar; private TextView title; private ImageView addview; private String userid,taskid; private Data_ReceiverNews.NewsBean newsBean; private Data_ReceiverNews data_receiverNews; private List newsBeanList = new ArrayList(); private List newsBeanListshort= new ArrayList(); private int allcount; private SwipeRefreshLayout uploadmore; private View footview; private int footerHeight; private boolean isshort; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activitychat); // userid = SharedPreferencesUtil.readUserid(Activity_Chat.this); // Intent intent = getIntent(); // taskid = intent.getStringExtra("taskidchat"); //这里参数我是为了测试写死的,正常可以通过intent或者读取缓存传值 userid = "02774bc536964386a68bd2b64145c910"; taskid = "eb05f06c46dd4acd87e0bef85575f981"; initview(); } private void initview() { lvChat = (ListView) findViewById(R.id.lv_chat); ekBar = (SimpleUserdefEmoticonsKeyBoard) findViewById(R.id.keyboard); uploadmore = (SwipeRefreshLayout) findViewById(R.id.uploadmore); toolbar = (Toolbar) findViewById(R.id.toobaraaa); setSupportActionBar(toolbar); footview = View.inflate(ActivityChat.this,R.layout.chat_footer,null); footview.measure(0,0); footerHeight = footview.getMeasuredHeight(); footview.setPadding(0,-footerHeight,0,0); lvChat.addFooterView(footview); getSupportActionBar().setDisplayShowTitleEnabled(false); showBack(); initEmoticonsKeyBoardBar(); initListView(); uploadmore.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_green_light, android.R.color.holo_orange_light); //上拉加载历史记录 uploadmore.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { try { OkHttpUtils.get(Http_Api.URL_NewReceiver) .params("userid",userid) .params("taskid",taskid) .execute(new StringCallback() { @Override public void onSuccess(String s, Call call, Response response) { // LogUtil.d("返回值",s); data_receiverNews = JsonUtil.parseJsonToBean(s,Data_ReceiverNews.class); allcount = Integer.parseInt(data_receiverNews.getResult()); if (allcount != 0){ if (newsBeanList!=null){ newsBeanList.clear(); } newsBeanList = data_receiverNews.getNews(); myChattingAdapter = new NyChattingListAdapter(ActivityChat.this,userid,newsBeanList); lvChat.setAdapter(myChattingAdapter); myChattingAdapter.notifyDataSetChanged(); uploadmore.setRefreshing(false); }else { // ToastUtil.showToast("对不起,没有更多消息了"); Toast.makeText(ActivityChat.this,"对不起没有更多消息了",Toast.LENGTH_SHORT).show(); uploadmore.setRefreshing(false); } } }); } catch (Exception e) { e.printStackTrace(); } } },2000); } }); } private void initEmoticonsKeyBoardBar() { SimpleCommonUtils.initEmoticonsEditText(ekBar.getEtChat()); ekBar.setAdapter(SimpleCommonUtils.getCommonAdapter(this, emoticonClickListener)); ekBar.addOnFuncKeyBoardListener(this); ekBar.addFuncView(new SimpleUserDefAppsGridView(this)); // ekBar.addFuncView(new SimpleAppsGridView(this)); ekBar.getEtChat().setOnSizeChangedListener(new EmoticonsEditText.OnSizeChangedListener() { @Override public void onSizeChanged(int w, int h, int oldw, int oldh) { scrollToBottom(); } }); ekBar.getBtnSend().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { OnSendBtnClick(ekBar.getEtChat().getText().toString()); ekBar.getEtChat().setText(""); } }); ekBar.getBtnVoice().setLongClickable(true); ekBar.getBtnVoice().setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // ToastUtil.showToast("功能未完善"); return false; } }); ekBar.getBtnVoice().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // ToastUtil.showToast("功能未完善"); } }); // ekBar.getEmoticonsToolBarView().addFixedToolItemView(false, R.drawable.icon_add, null, new View.OnClickListener() { // @Override // public void onClick(View v) { // Toast.makeText(Activity_Chat.this, "ADD", Toast.LENGTH_SHORT).show(); // } // }); // ekBar.getEmoticonsToolBarView().addToolItemView(R.drawable.icon_setting, new View.OnClickListener() { // @Override // public void onClick(View v) { // Toast.makeText(Activity_Chat.this, "SETTING", Toast.LENGTH_SHORT).show(); // } // }); } //表情点击事件 EmoticonClickListener emoticonClickListener = new EmoticonClickListener() { @Override public void onEmoticonClick(Object o, int actionType, boolean isDelBtn) { if (isDelBtn) { SimpleCommonUtils.delClick(ekBar.getEtChat()); } else { if(o == null){ return; } if(actionType == Constants.EMOTICON_CLICK_BIGIMAGE){ if(o instanceof EmoticonEntity){ OnSendImage(((EmoticonEntity)o).getIconUri()); } } else { String content = null; if(o instanceof EmojiBean){ content = ((EmojiBean)o).emoji; } else if(o instanceof EmoticonEntity){ content = ((EmoticonEntity)o).getContent(); } if(TextUtils.isEmpty(content)){ return; } int index = ekBar.getEtChat().getSelectionStart(); Editable editable = ekBar.getEtChat().getText(); editable.insert(index, content); } } } }; private void initListView() { ShowNow(); uploadmoreadd(); } //显示界面的联网操作 private void ShowNow() { try { OkHttpUtils.get(Http_Api.URL_NewReceiver) .params("userid",userid) .params("taskid",taskid) .execute(new StringCallback() { @Override public void onSuccess(String s, Call call, Response response) { // LogUtil.d("返回值",s); data_receiverNews = JsonUtil.parseJsonToBean(s,Data_ReceiverNews.class); allcount = Integer.parseInt(data_receiverNews.getResult()); if (data_receiverNews.getNews()!=null){ newsBeanList = data_receiverNews.getNews(); } // LogUtil.d("集合:newsBeanList--",newsBeanList+""); if (allcount>28){ //设置最多显示28条数据也就是4页,然后下拉加载历史数据,如果不够28条数据有多少展示多少 newsBeanListshort = newsBeanList.subList(newsBeanList.size()-28,newsBeanList.size()); myChattingAdapter = new NyChattingListAdapter(ActivityChat.this,userid,newsBeanListshort); isshort = true; }else { myChattingAdapter = new NyChattingListAdapter(ActivityChat.this,userid,newsBeanList); isshort = false; } lvChat.setAdapter(myChattingAdapter); } }); } catch (Exception e) { e.printStackTrace(); } } //上拉加载最新数据 private void uploadmoreadd() { lvChat.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE){ ekBar.reset(); int lastposition = lvChat.getLastVisiblePosition();//最后一个item的位置 if (lastposition == lvChat.getCount() - 1){ footview.setPadding(0,0,0,footerHeight); Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { try { OkHttpUtils.get(Http_Api.URL_NewReceiver) .params("userid",userid) .params("taskid",taskid) .execute(new StringCallback() { @Override public void onSuccess(String s, Call call, Response response) { // LogUtil.d("返回值",s); data_receiverNews = JsonUtil.parseJsonToBean(s,Data_ReceiverNews.class); allcount = Integer.parseInt(data_receiverNews.getResult()); if (newsBeanList!=null){ newsBeanList.clear(); } newsBeanList = data_receiverNews.getNews(); if (allcount>28){ newsBeanListshort = newsBeanList.subList(newsBeanList.size()-28,newsBeanList.size()); myChattingAdapter = new NyChattingListAdapter(ActivityChat.this,userid,newsBeanListshort); isshort = true; }else { myChattingAdapter = new NyChattingListAdapter(ActivityChat.this,userid,newsBeanList); isshort = false; } lvChat.setAdapter(myChattingAdapter); myChattingAdapter.notifyDataSetChanged(); Toast.makeText(ActivityChat.this,"加载完成",Toast.LENGTH_SHORT).show(); // ToastUtil.showToast("加载完成"); } }); } catch (Exception e) { e.printStackTrace(); } footview.setPadding(0,-footerHeight,0,0); } },2000); } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // boolean enable = false; // if(lvChat != null && lvChat.getChildCount() > 0){ // // 检查列表的第一个项目是否可见 // boolean firstItemVisible = lvChat.getFirstVisiblePosition() == 0; // // 检查第一个项目的顶部是否可见 // boolean topOfFirstItemVisible = lvChat.getChildAt(0).getTop() == 0; // // 启用或禁用刷新布局 // enable = firstItemVisible && topOfFirstItemVisible; // } // uploadmore.setRefreshing(enable); } }); } //用当前时间 private String getTime() { Date date = new Date(System.currentTimeMillis()); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm ss"); return dateFormat.format(date); } //发送信息联网 private void OnSendBtnClick(String msg) { if (!TextUtils.isEmpty(msg)) { final Data_ReceiverNews.NewsBean bean = new Data_ReceiverNews.NewsBean(); bean.setNewscontent(msg); bean.setCreatime(getTime()); // LogUtil.d("------Emoji:",msg); try { OkHttpUtils.post(Http_Api.URL_NewSend) .params("taskid",taskid) .params("userid",userid) .params("newscontent",msg) .execute(new StringCallback() { @Override public void onSuccess(String s, Call call, Response response) { // LogUtil.d("返回值",s); Data_uploadBack_tag back_tag = JsonUtil.parseJsonToBean(s,Data_uploadBack_tag.class); if(back_tag.getResult().equals("0")){ // ToastUtil.showToast("非法的表情符号!"); Toast.makeText(ActivityChat.this,"非法的表情符号!",Toast.LENGTH_SHORT).show(); }else if (back_tag.getResult().equals("1")){ // if (isshort){ // if (bean!= null){ // newsBeanListshort.add(bean); // } // // }else { // if (bean!=null){ // newsBeanList.add(bean); // } // } // myChattingAdapter.setsendtype(1); ShowNow();//这里选择重新联网刷新列表,而不是在集合里手动添加数据 myChattingAdapter.notifyDataSetChanged(); scrollToBottom(); } } }); } catch (Exception e) { e.printStackTrace(); } } } /** * 版本号小于21的后退按钮图片 */ private void showBack(){ //setNavigationIcon必须在setSupportActionBar(toolbar);方法后面加入 toolbar.setNavigationIcon(R.drawable.ic_back); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); } private void OnSendImage(String image) { if (!TextUtils.isEmpty(image)) { OnSendBtnClick("[img]" + image);//给大图片加标记 } } private void scrollToBottom() {//设置滚动到底部 lvChat.requestLayout(); lvChat.post(new Runnable() { @Override public void run() { lvChat.setSelection(lvChat.getBottom()+2); } }); } @Override public void OnFuncPop(int i) { scrollToBottom(); } @Override public void OnFuncClose() { } @Override protected void onPause() { super.onPause(); ekBar.reset(); }}

最后 感谢阅读 demo下载地址
https://github.com/PangHaHa12138/TestChatdemo

你可能感兴趣的:(安卓开发-最简单快速的仿微信聊天实现-附赠微信原生表情,QQ原生表情)