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,并且都是本人一行一行码出来的.

更多相关文章

  1. ---RIL层代码分析---RIL_startEventLoop()->eventLoop()->ril_ev
  2. Android:UI更新方法三:Handler+Worker Thread
  3. android handler和message的常用方法
  4. Androidpn推送解决方案
  5. Android(安卓)MaoZhuaWeiBo Adapter ListView优化及发送微博 - 4
  6. Android(安卓)程式开发:(廿一)消息传递 —— 21.3 使用Intent发送短
  7. Android客户端异常检测
  8. Android应用程序键盘(Keyboard)消息处理机制分析(三)
  9. Android:Android基础知识

随机推荐

  1. VLC Android播放器介绍
  2. Linux Kernel and Android(安卓)休眠与唤
  3. android 命令介绍
  4. textview中加链接
  5. android 静音与振动
  6. ImageView androidscaleType属性详解
  7. 关于Android的keystore生成和查看keystor
  8. Android(安卓)的 Tweened animation
  9. Android(安卓): dip/dp与px(pixel)之间的
  10. 如何在Windows下搭建Android开发环境