Android 极光IM简单的聊天界面全手动实现

 

 

                   Android 极光IM简单的聊天界面全手动实现

说到实时通讯,很多人都想到融云,极光,环信,网易啊等等一系列,

因为需求原因,我们最近的项目呢是用的极光.

由于极光的界面Demo十分繁琐,很多功能我们用不到,所以我干脆自己写了会话列表和聊天界面

首先呢,消息展示和踏板是这样的:

接下来说一下实现的过程:

极光IM的集成呢我这边就不多说了,官网写的又简单又详细.

界面的XML

以下是Activity的布局文件,消息列表我选择用RecyclerView来实现

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="@color/beijing">
   <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/Title_Height"
        android:background="@color/white">

        <ImageView
            android:id="@+id/jg_details_back"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_centerVertical="true"
            android:paddingLeft="30px"
            android:paddingRight="60px"
            android:src="@mipmap/back" />
        <TextView
            android:id="@+id/jg_details_title"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text=""
            android:textColor="@color/heise"
            android:textSize="@dimen/Title_TextSize" />

    </RelativeLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/jg_details_recy"
        android:layout_width="match_parent"
        android:layout_height="0px"
        android:layout_weight="1"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/fenge" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="@color/white"
        android:paddingLeft="30px"
        android:paddingRight="30px">


        <EditText
            android:id="@+id/jg_details_edit"
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20px"
            android:layout_marginTop="20px"
            android:layout_weight="1"
            android:background="@mipmap/sousuo1"
            android:gravity="center_vertical"
            android:hint="请输入咨询的问题"
            android:imeOptions="actionSend"
            android:paddingLeft="20px"
            android:paddingRight="20px"
            android:paddingTop="10px"
            android:paddingBottom="10px"
            android:singleLine="true"
            android:textColor="@color/huise"
            android:textCursorDrawable="@null"
            android:textSize="14sp" />

        <ImageView
            android:id="@+id/jg_details_img"
            android:layout_width="80px"
            android:layout_marginTop="20px"
            android:layout_height="80px"
            android:layout_marginLeft="20px"
            android:src="@mipmap/send_img" />

    </LinearLayout>
</LinearLayout>

控件初始化:

 这些代码 放在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<Conversation> 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<Message> 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<Message> 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 它俩的作用是把消息挤到对应地方

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/beijing"
    android:orientation="vertical">

    <TextView
        android:id="@+id/item_jg_details_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:padding="8px"
        android:textColor="@color/huise"
        android:textSize="12sp"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/item_jg_details_head"
            android:layout_width="180px"
            android:layout_height="180px"
            android:paddingBottom="30px"
            android:paddingLeft="30px"
            android:paddingTop="30px"
            android:visibility="gone" />


        <LinearLayout
            android:layout_width="0px"
            android:layout_height="wrap_content"
            android:layout_margin="40px"
            android:layout_weight="1"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/item_jg_details_tc"
                android:layout_width="0px"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:visibility="visible" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/item_jg_details_content"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="30px"
                    android:layout_marginTop="40px"
                    android:paddingBottom="10px"
                    android:paddingLeft="18px"
                    android:paddingRight="18px"
                    android:paddingTop="10px"
                    android:textColor="@color/heise"
                    android:textSize="15sp"
                    android:visibility="gone" />

                <com.makeramen.roundedimageview.RoundedImageView
                    android:id="@+id/item_jg_details_img"
                    xmlns:app="http://schemas.android.com/apk/res-auto"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:scaleType="fitCenter"
                    app:riv_corner_radius="12px"
                    app:riv_mutate_background="true"
                    app:riv_oval="false"
                    app:riv_tile_mode="repeat" />
                <ImageView

                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:visibility="gone" />
                <TextView
                    android:id="@+id/item_jg_details_state"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:padding="8px"
                    android:textSize="12sp"
                    android:visibility="gone" />
            </LinearLayout>

            <TextView
                android:id="@+id/item_jg_details_tc1"
                android:layout_width="0px"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:visibility="gone" />

        </LinearLayout>

        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/item_jg_details_head1"
            android:layout_width="180px"
            android:layout_height="180px"
            android:paddingBottom="30px"
            android:paddingRight="30px"
            android:paddingTop="30px"
            android:visibility="gone" />


    </LinearLayout>




</LinearLayout >

注意事项啊:

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

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页