现在越来越多的手机软件具备社交聊天功能,所以聊天界面的使用便变得很频繁,下面我们将自己实现一个简单的类似QQ的聊天界面。
首先来看整个工程的目录结构:
目录结构很简单,主要难点在Adapter。
然后看实现的效果图:
从效果图上看出,界面底部用了一个EditText和一个Button,水平分布,上面放了一个RecyclerView,布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="#ede9e9"
android:orientation="vertical"
tools:context="com.demo.sisyphus.hellorobot.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="机器人"
android:textSize="10pt"
android:gravity="center_horizontal"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_chat"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rect"
android:layout_weight="1"
android:layout_marginRight="5dp"
android:id="@+id/et_msg"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn_send"
android:text="@string/btnsend"/>
</LinearLayout>
</LinearLayout>
然后看这篇文章的重点Adapter,用过RecyclerView的都知道,它的数据处理都在Adapter,类似ListView的Adapter,但是更加强大,代码如下:
public class ChatAdapter extends RecyclerView.Adapter {
private Context context;
private static final int ME = 0;
private static final int OTHRE = 1;
private List<Msg> list = new ArrayList<>();
public ChatAdapter(Context context, ArrayList<Msg> list){
this.context = context;
this.list = list;
}
class ViewHolder extends RecyclerView.ViewHolder {
LinearLayout me;
LinearLayout other;
public ViewHolder(View itemView) {
super(itemView);
me = (LinearLayout) itemView.findViewById(R.id.me);
other = (LinearLayout) itemView.findViewById(R.id.other);
}
public LinearLayout getMe() {
return me;
}
public LinearLayout getOther() {
return other;
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewHolder viewHolder = null;
switch (viewType){
case ME:
viewHolder = new ViewHolder(LayoutInflater.from(context).inflate(R.layout.chat_item2, parent, false));
break;
case OTHRE:
viewHolder = new ViewHolder(LayoutInflater.from(context).inflate(R.layout.chat_item, parent, false));
break;
}
return viewHolder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ViewHolder viewHolder = (ViewHolder) holder;
TextView tv = new TextView(context);
Msg msg = list.get(position);
tv.setText(msg.getMsg());
switch (msg.getType()){
case ME:
/**
* 当recyclerview承载的数据过多的时候,去滑动recyclerview,
* 划出屏幕以外的item会重新绑定数据,如果这个时候绑定数据的方式是
* viewgroup的addView()方法的话,会出现item添加很多重复的view
* 所以这之前应该执行清除里面view的操作,即removeAllViews()
*/
viewHolder.getMe().removeAllViews();
tv.setBackgroundResource(R.mipmap.chat_me);
viewHolder.getMe().addView(tv);
break;
case OTHRE:
viewHolder.getOther().removeAllViews();
tv.setBackgroundResource(R.mipmap.chat_other);
viewHolder.getOther().addView(tv);
break;
}
}
@Override
public int getItemCount() {
return list.size();
}
@Override
public int getItemViewType(int position) {
return list.get(position).getType() == 0 ? ME : OTHRE;
}
public void addMsg(Msg msg){
list.add(msg);
}
}
因为是聊天界面,所以需要RecyclerView根据发出消息的人是谁来判断这条消息是显示左边还是右边,因此就需要根据ViewType的值来加载两个不同的布局文件,在这个项目中分别是chat_item.xml和chat_item2.xml。而消息和消息类型的判断我们封装成一个对象,即Msg类:
public class Msg {
private String msg;
private int type;
public Msg(String msg, int type){
this.msg = msg;
this.type = type;
}
public String getMsg() {
return msg;
}
public int getType() {
return type;
}
}
这个类只有两个属性,很简单,主要承载消息内容和消息类型(来自谁),消息内容可根据实际需要扩展为图片、音频之类的。
最后在Activity里面,将所有控件初始化之后,我们就实现了这个简单的聊天界面。
另外,为了使这个聊天界面聊天界面更加具有可用性,我们可以给消息加上定位的效果,即当消息过多时,RecyclerView自动滑动到当前消息的位置,使用RecyclerView的smoothScrollToPosition(position)方法即可实现,很简单。
最后补充两个点:
**1. 圆形头像
这个圆形头像使用了一个开源库RoundedImageView
android studio直接在项目上右键-Open Module Settings-Dependencies-Add Library dependency,直接搜索RoundedImageView就可以了,选择最新版本。
使用很简单,引入RoundedImageView控件,设置其属性就可以得到多种形状的图片了。
2. 我做这个项目遇到的一个问题
在做这个Demo时,我发现,当聊天数据量达到十多条时,滑动RecyclerView,一些item会一直重复添加数据项,后来我发现是绑定数据的时候没有考虑周全,出现问题的地方就是我Adapter写注释的地方,原因和解决办法可以参考注释。
好了,一个简单的聊天界面就大功告成,感兴趣的朋友可以点击这里下载源码,我放在github上的。
可能还有很多代码Bug和说明不正确的地方,请批评指正。**