Android(安卓)极光IM简单的聊天界面全手动实现
Android 极光IM简单的聊天界面全手动实现
说到实时通讯,很多人都想到融云,极光,环信,网易啊等等一系列,
因为需求原因,我们最近的项目呢是用的极光.
由于极光的界面Demo十分繁琐,很多功能我们用不到,所以我干脆自己写了会话列表和聊天界面
首先呢,消息展示和踏板是这样的:
接下来说一下实现的过程:
极光IM的集成呢我这边就不多说了,官网写的又简单又详细.
界面的XML
以下是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);
这边涉及到一个消息接收的监听类GlobalEventListener
这里面存放了两个静态的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(); } }}
接下来说一下数据加载,也就是监听到新数据调用的 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和ViewHolder
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()); } }}
BaseViewHolder
这个类是我封装的一个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);}
最后说一下ViewHoler对应的的布局文件 item_jg_details
这里面主要原理是 左右各放一个头像 , 根据用户是自己还是别人,来区分隐藏哪一边,
中间竖向排列了文本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,并且都是本人一行一行码出来的.
更多相关文章
- ---RIL层代码分析---RIL_startEventLoop()->eventLoop()->ril_ev
- Android:UI更新方法三:Handler+Worker Thread
- android handler和message的常用方法
- Androidpn推送解决方案
- Android(安卓)MaoZhuaWeiBo Adapter ListView优化及发送微博 - 4
- Android(安卓)程式开发:(廿一)消息传递 —— 21.3 使用Intent发送短
- Android客户端异常检测
- Android应用程序键盘(Keyboard)消息处理机制分析(三)
- Android:Android基础知识