说到实时通讯,很多人都想到融云,极光,环信,网易啊等等一系列,
因为需求原因,我们最近的项目呢是用的极光.
由于极光的界面Demo十分繁琐,很多功能我们用不到,所以我干脆自己写了会话列表和聊天界面
首先呢,消息展示和踏板是这样的:
接下来说一下实现的过程:
极光IM的集成呢我这边就不多说了,官网写的又简单又详细.
以下是Activity的布局文件,消息列表我选择用RecyclerView来实现
这些代码 放在Activity的onCreate方法中就可以了,
其中涉及到的东西都会在下面讲到
title = findViewById(R.id.jg_details_title);
mEdit = findViewById(R.id.jg_details_edit);
mRecycler = findViewById(R.id.jg_details_recy);
mRecycler.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new JG_details_Adapter(this);
mRecycler.setAdapter(mAdapter);
position = getIntent().getIntExtra("position", 0);
//设置消息接收 监听
GlobalEventListener.setJG(this, false);
//进入会话状态,不接收通知栏
JMessageClient.enterSingleConversation(this.userName);
这里面存放了两个静态的activity实体,一个是会话列表,一个是会话详情.
这样做呢,是方便消息接到的第一时间内,能调用到initData()来刷新数据
刚封装这个类的时候,刷新数据我是想用EventBus的,但是后来一想,定义两个静态对象要简明直接的多
/*
*作者:赵星海
*时间:18/11/21 16:08
*用途:极光IM消息接收处理
*/
public class GlobalEventListener {
private Context MainContext;
private static Activity_JG JG_list = null; // 会话列表对象
private static Activity_JG_details JG_details = null;// 会话详情对象
public GlobalEventListener(Context context) {
MainContext = context;
JMessageClient.registerEventReceiver(this);
}
public static void setJG(Activity activity, boolean islist) {
if (islist) {
JG_list = (Activity_JG) activity;
} else {
JG_details = (Activity_JG_details) activity;
}
}
//通知点击 前往会话列表
public void onEvent(NotificationClickEvent event) {
MainContext.startActivity(new Intent(MainContext, Activity_JG.class));
}
// 接收消息 (主线程)(刷新UI)
public void onEventMainThread(MessageEvent event){
if (JG_details != null) {
JG_details.initData();
} else if (JG_list != null) {
JG_list.initData();
}
}
}
在activity中写一个这样的方法 负责数据加载和消息接收类 调用刷新
public void initData() {
List msgList = JMessageClient.getConversationList();
if (msgList != null) {
if (msgList.size() > 0) {
if (msgList.get(position) != null) {
conversation = msgList.get(position);
//重置会话未读消息数
conversation.resetUnreadCount();
}
}
}
if (conversation != null) {
title.setText(conversation.getTitle() == null ? "" : conversation.getTitle());
UserInfo info = (UserInfo) conversation.getTargetInfo();
userName = info.getUserName();
//userName = "f8443445-a7ef-47d8-8005-b0d57851b396"; //todo 可自定义
//使列表滚动到底部
if (conversation.getAllMessage() != null) {
if (conversation.getAllMessage().size() > 0) {
mAdapter.setData(conversation.getAllMessage());
//设置刷新不闪屏
((SimpleItemAnimator) mRecycler.getItemAnimator()).setSupportsChangeAnimations(false);
if (one) {
mAdapter.notifyDataSetChanged();
} else {
mAdapter.notifyItemInserted(conversation.getAllMessage().size() - 1);
}
mRecycler.scrollToPosition(conversation.getAllMessage().size() - 1);
}
}
mAdapter.setOnItemClickListener(new JG_details_Adapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
switch (view.getId()) {
case R.id.item_jg_details_img:
ImageContent imageContent = (ImageContent) conversation.getAllMessage().get(position).getContent();
startActivity(new Intent(Activity_JG_details.this, Activity_img.class)
.putExtra("ImgUrl", imageContent.getLocalThumbnailPath()));
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);//动画
break;
}
}
});
}
one=false; // 代表不是第一次initData
}
Adapter这边的逻辑相对简单,只是定义了一个点击响应
/**
* Created by Xinghai.Zhao on 18/11/19.
*/
/*
*作者:赵星海
*用途: 极光聊天页面Adapter
*/
public class JG_details_Adapter extends RecyclerView.Adapter {
private OnItemClickListener mOnItemClickListener = null;
private Context MyContext;
private List mList;
public JG_details_Adapter(Context context) {
this.MyContext = context;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater from = LayoutInflater.from(MyContext);
View view = from.inflate(R.layout.item_jg_details, parent, false);
return new JG_details_holder(view, MyContext,mOnItemClickListener);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
JG_details_holder holder1 = (JG_details_holder) holder;
if (mList!=null||mList.size()>0){
holder1.setHolderData(mList.get(position),position);
//将position保存在itemView的Tag中,以便点击时进行获取 ----------------------
holder.itemView.setTag(position);
}
}
@Override
public int getItemCount() {
return mList == null ? 0 : mList.size();
}
public void setOnItemClickListener(OnItemClickListener listener) {//-------------
this.mOnItemClickListener = listener;
}
public void setData(List data) {
this.mList = data;
}
public void removeItem(int position) {
mList.remove(position);
}
//define interface
public interface OnItemClickListener { //--------------------------
void onItemClick(View view, int position);
}
}
ViewHolder这边,我的逻辑是:
自己的消息显示 (已读/未读)状态,对方发来的消息则不显示.
/**
* Created by Xinghai.Zhao on 18/04/02.
*/
/*
*作者:赵星海
*时间:18/11/27 17:52
*用途:极光聊天页面Holder
*/
public class JG_details_holder extends BaseViewHolder implements View.OnClickListener {
private RoundedImageView MyImg; //发送的图片
private TextView MyTv_content, MyTV_Time, My_tc, My_tc1, My_Tv_state;
private CircleImageView MyHead;
private Context MyContext;
private JG_details_Adapter.OnItemClickListener mOnItemClickLis = null;
private View view;
public JG_details_holder(View itemView, Context con, JG_details_Adapter.OnItemClickListener mOnItemClick) {
super(itemView);
MyContext = con;
mOnItemClickLis = mOnItemClick;
}
@Override
public void findView(View view) {
this.view = view;
MyImg = this.view.findViewById(R.id.item_jg_details_img);//图片
MyHead = view.findViewById(R.id.item_jg_details_head); //头像
MyTv_content = view.findViewById(R.id.item_jg_details_content);//内容
MyTV_Time = view.findViewById(R.id.item_jg_details_time); // 时间
My_tc = view.findViewById(R.id.item_jg_details_tc);
My_tc1 = view.findViewById(R.id.item_jg_details_tc1);
My_Tv_state = view.findViewById(R.id.item_jg_details_state);
MyImg.setOnClickListener(this);
MyHead.setOnClickListener(this);
MyTv_content.setOnClickListener(this);
MyTV_Time.setOnClickListener(this);
My_Tv_state.setOnClickListener(this);
}
@TargetApi(Build.VERSION_CODES.M)
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void setHolderData(Object o, int position) {
if (o != null) {
Message bean = (Message) o;
if (bean.getFromUser() != null) {
if (bean.getFromUser().getUserName().equals(UserUtils.id)) {
//是自己的聊天
MyHead = view.findViewById(R.id.item_jg_details_head1); //头像 右边
MyHead.setVisibility(View.VISIBLE);//头像显示隐藏
view.findViewById(R.id.item_jg_details_head).setVisibility(View.GONE);
//内容背景
MyTv_content.setBackground(MyContext.getDrawable(R.drawable.textview_jg_msg_i));
MyTv_content.setTextColor(MyContext.getColor(R.color.white));
My_tc.setVisibility(View.VISIBLE);//权重挤压
My_tc1.setVisibility(View.GONE);
//对方是否未读
My_Tv_state.setVisibility(View.VISIBLE);
if (bean.haveRead()) {
My_Tv_state.setText("已读");
My_Tv_state.setTextColor(MyContext.getColor(R.color.blue));
}
{
My_Tv_state.setText("未读");
My_Tv_state.setTextColor(MyContext.getColor(R.color.huise));
}
} else {
My_Tv_state.setVisibility(View.GONE);//对方是否未读
MyHead = view.findViewById(R.id.item_jg_details_head); //头像
MyHead.setVisibility(View.VISIBLE);//头像显示隐藏
view.findViewById(R.id.item_jg_details_head1).setVisibility(View.GONE);
//内容背景
MyTv_content.setBackground(MyContext.getDrawable(R.drawable.textview_jg_msg_he));
MyTv_content.setTextColor(MyContext.getColor(R.color.heise));
My_tc.setVisibility(View.GONE);
My_tc1.setVisibility(View.VISIBLE);
}
MyHead.setOnClickListener(this); //刷新头像点击事件
//头像
bean.getFromUser().getAvatarBitmap(new GetAvatarBitmapCallback() {
@Override
public void gotResult(int i, String s, Bitmap bitmap) {
if (bitmap != null) {
MyHead.setImageBitmap(bitmap);
} else {
Log.e("极光会话详情-用户头像赋值", "bitmap为空!");
}
}
});
switch (bean.getContentType()) {
case text:
MyTv_content.setVisibility(View.VISIBLE);
MyTV_Time.setVisibility(View.GONE);
MyImg.setVisibility(View.GONE);
//内容
TextContent textContent = (TextContent) bean.getContent();
String text = textContent.getText();
MyTv_content.setText(text);
break;
case image:
MyTv_content.setVisibility(View.GONE);
MyTV_Time.setVisibility(View.GONE);
MyImg.setVisibility(View.VISIBLE);
ImageContent imageContent = (ImageContent) bean.getContent();
if (imageContent.getLocalThumbnailPath() != null) {
Glide.with(MyContext).load(imageContent.getLocalThumbnailPath()).into(MyImg);
}
break;
case prompt: //提示
MyTv_content.setVisibility(View.GONE);
MyTV_Time.setVisibility(View.VISIBLE);
MyImg.setVisibility(View.GONE);
//内容
PromptContent promptContent = (PromptContent) bean.getContent();
String promptText = promptContent.getPromptText();
MyTV_Time.setText(promptText);
break;
}
}
}
}
@Override
public void onClick(View v) {
if (mOnItemClickLis != null) {
mOnItemClickLis.onItemClick(v, getPosition());
}
}
}
这个类是我封装的一个holder上层类 主要是为了代码逻辑区分,一目了然.. 等等作用.
/**
* Created by Xinghai.Zhao on 18/03/29.
*/
/*
*作者:赵星海
*时间:18/03/29 16:57
*用途:ViewHolder上层类
*/
public abstract class BaseViewHolder extends RecyclerView.ViewHolder{
public BaseViewHolder(View itemView) {
super(itemView);
findView(itemView);
}
public abstract void findView(View view);
public abstract void setHolderData(Object o,int position);
}
这里面主要原理是 左右各放一个头像 , 根据用户是自己还是别人,来区分隐藏哪一边,
中间竖向排列了文本textView,图片imageView和 提醒view("已读/未读")
根据消息类型绝对显示隐藏哪一个
中间还放置了权重挤压view: item_jg_details_tc 和 item_jg_details_tc 它俩的作用是把消息挤到对应地方
注意事项啊:
item中的头像使用了三方的CircleImageView,圆角图片使用了RoundedImageView
这俩控件直接写两行依赖就可以使用:
// 头像切圆
implementation 'de.hdodenhof:circleimageview:2.2.0'
//圆角图片
compile 'com.makeramen:roundedimageview:2.2.1'
如需扩展使用,还需要注意以下几点:
Adapter的点击事件中的 Activity_img 这个不用我多解释了吧,有了极光拿到的图片地址:ImgUrl
跳到Activity_img页面全屏加载一下加一个缩放手势就好了; 实在不会的我给你指条明路:PhotoView 百度搜去吧
再多说几句啊,Item这边我多次使用了view的现隐功能 setVisibility(View.VISIBLE);
如需要扩展,请多留意
@Override
protected void onDestroy() {
//退出会话界面 (开始接收通知栏)
JMessageClient.exitConversation();
//设置消息接收 监听
GlobalEventListener.setJG(null, false);
super.onDestroy();
}
关于以上代码不明白的,或者我没有说清楚的,可以在评论区问我.
声明:
以上代码是从项目中摘出来的,不存在Demo,并且都是本人一行一行码出来的.