前言;正常实现聊天功能想必大家都使用三方的Sdk比如环信融云集成的,但是聊天记录的保存只能有三天,想增加保存时长就需要花钱,so 我只好自己想办法实现了,这个demo是类似于留言板,并非即时通讯!只实现了表情文字图文混排,可以通过手动刷新实现即时通讯ok废话少说,先看效果图;
项目下载地址
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页,上来初始化页面也是,然后可以通过聊完上拉不停的重复手动实现即时通讯
当然这是开玩笑了
下面上代码:
主要布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/titlefragment"
android:name="com.panghaha.it.testchatdemo.Titlefragment"
android:layout_width="match_parent"
android:layout_height="45dp"/>
<!--<TextView-->
<!--android:layout_centerInParent="true"-->
<!--android:textSize="22sp"-->
<!--android:text="在未来的日子里,\n努力让抛弃我的人\n始终觉得她们的决定是正确的"-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content" />-->
<FrameLayout
android:id="@+id/content"
android:layout_above="@+id/bottom_navigation_bar"
android:layout_below="@+id/titlefragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
<com.ashokvarma.bottomnavigation.BottomNavigationBar
android:id="@+id/bottom_navigation_bar"
android:layout_gravity="bottom"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
主界面,下面四个按钮,切换四个fragment
聊天界面布局 最外层用自己继承
XhsEmoticonsKeyBoard 键盘类
<com.panghaha.it.testchatdemo.common.SimpleUserdefEmoticonsKeyBoard
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:id="@+id/keyboard"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:background="@drawable/pic_bg3x"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--<include layout="@layout/activity_right_toobar"/>-->
<android.support.v7.widget.Toolbar
android:id="@+id/toobaraaa"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/titbar"
android:minHeight="?attr/actionBarSize">
<TextView
android:id="@+id/toolbarmtit"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:ellipsize="end"
android:text="安琪宝贝"
android:scrollHorizontally="true"
android:textColor="@color/white"
android:layout_gravity="center" />
<!--自定义toolbar的title 和subtitle -->
</android.support.v7.widget.Toolbar>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/uploadmore"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv_chat"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#00000000"
android:divider="@null"
android:fadingEdge="none"
android:fitsSystemWindows="true"
android:listSelector="#00000000"
android:scrollbarStyle="outsideOverlay"
android:scrollingCache="false"
android:smoothScrollbar="true"
android:stackFromBottom="true" />
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
</com.panghaha.it.testchatdemo.common.SimpleUserdefEmoticonsKeyBoard>
键盘类
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<EmojiBean> emojiArray = new ArrayList<>();
Collections.addAll(emojiArray, DefEmoticons.sEmojiArray);
EmoticonPageSetEntity emojiPageSetEntity
= new EmoticonPageSetEntity.Builder()
.setLine(3)
.setRow(7)
.setEmoticonList(emojiArray)
.setIPageViewInstantiateItem(getDefaultEmoticonPageViewInstantiateItem(new EmoticonDisplayListener<Object>() {
@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<EmoticonPageEntity>() {
@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<Object> getEmoticonDisplayListener(final EmoticonClickListener emoticonClickListener){
return new EmoticonDisplayListener<Object>() {
@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<EmoticonEntity> 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<EmoticonEntity> 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<EmoticonPageEntity> getDefaultEmoticonPageViewInstantiateItem(final EmoticonDisplayListener<Object> emoticonDisplayListener) {
return getEmoticonPageViewInstantiateItem(EmoticonsAdapter.class, null, emoticonDisplayListener);
}
public static PageViewInstantiateListener<EmoticonPageEntity> getEmoticonPageViewInstantiateItem(final Class _class, EmoticonClickListener onEmoticonClickListener) {
return getEmoticonPageViewInstantiateItem(_class, onEmoticonClickListener, null);
}
public static PageViewInstantiateListener<EmoticonPageEntity> getEmoticonPageViewInstantiateItem(final Class _class, final EmoticonClickListener onEmoticonClickListener, final EmoticonDisplayListener<Object> emoticonDisplayListener) {
return new PageViewInstantiateListener<EmoticonPageEntity>() {
@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<Object> getCommonEmoticonDisplayListener(final EmoticonClickListener onEmoticonClickListener, final int type) {
return new EmoticonDisplayListener<Object>() {
@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<Data_ReceiverNews.NewsBean> mData;
private String myuserid;
public int isMesend;
public int istext;
public NyChattingListAdapter(Activity activity,String id,List<Data_ReceiverNews.NewsBean> 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 ━━━
* <p/>
* 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<Data_ReceiverNews.NewsBean> newsBeanList = new ArrayList<Data_ReceiverNews.NewsBean>();
private List<Data_ReceiverNews.NewsBean> newsBeanListshort= new ArrayList<Data_ReceiverNews.NewsBean>();
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