RecyclerView实现聊天界面
-
先看一下效果图
-
RecyclerView简介
RecyclerView是从Android5.0开始,谷歌公司推出的一个用于大量数据展示的新控件。RecyclerView可以替代传统的ListView,它更加强大和灵活,RecyclerView的官方定义如下
A flexible view for providing a limited window into a large data set
从定义可以看出flexible是其优点,RecyclerView是support-v7包中的新组件。
RecyclerView相对于ListView的优点如下:
1.RecyclerView封装了viewHolder的回收复用,也就是说Recyclerview标准化了viewHolder。
2.设置布局管理器以控制item的布局方式,横向、竖向以及瀑布流方式,可以调用setLayoutManager设置布局方式。RecyclerView的布局管理器有LinearLayoutManager、GridLayoutManager和StaggeredGridLayoutManager。也可以调用布局管理器的setOrientation方法设置recyclerView的滚动方向。
3.可以设置RecyclerView的间隔样式,通过继承RecyclerView的ItemDecoration这个类,去自定义间隔样式,然后调用addItemDecoration去添加间隔样式。
4.可以控制item的增删动画,通过ItemAnimator这个类进行控制,调用setItemAnimator方法实现。
-
实现原理
public class RecycleViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ private List<ChatWithFriendBean> mList; private Context mContext; private LayoutInflater mInflater; private AdapterView.OnItemClickListener mListener; private RequestOptions options; public interface OnItemClickListener{ void onItemClick(ChatWithFriendBean bean); } public RecycleViewAdapter(Context context,List<ChatWithFriendBean> list) { mContext = context; mList = list; mInflater = LayoutInflater.from(mContext); options = new RequestOptions().centerCrop(); } //通过不同的viewType创建不同的viewHolder @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { if (viewType == ITEM_TYPE.TYPE_FRIEND_MSG.ordinal()) { return new FromMsgHolder(mInflater.inflate(R.layout.item_chat_friend,viewGroup,false)); }else { return new ToMsgHolder(mInflater.inflate(R.layout.item_chat_me,viewGroup,false)); } } //viewHolder和数据进行绑定 @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { ChatWithFriendBean chatWithFriendBean = mList.get(i); int itemViewType = getItemViewType(i); if (itemViewType == ITEM_TYPE.TYPE_FRIEND_MSG.ordinal()) { Glide.with(mContext).load(mContext.getResources().getDrawable(R.drawable.small_cat)).into(((FromMsgHolder)viewHolder).headImage); if (chatWithFriendBean.getMapUrl() != null && !"".equals(chatWithFriendBean.getMapUrl())) { ((FromMsgHolder)viewHolder).llAddressInfo.setVisibility(View.VISIBLE); ((FromMsgHolder)viewHolder).content.setVisibility(View.GONE); ((FromMsgHolder)viewHolder).addressContent.setText(chatWithFriendBean.getPoiname() + "\n" + chatWithFriendBean.getLabel()); Glide.with(mContext).load(chatWithFriendBean.getMapUrl()).apply(options).into(((FromMsgHolder)viewHolder).address); }else { ((FromMsgHolder)viewHolder).llAddressInfo.setVisibility(View.GONE); ((FromMsgHolder)viewHolder).content.setVisibility(View.VISIBLE); ((FromMsgHolder)viewHolder).content.setText(chatWithFriendBean.getContent()); } }else { Glide.with(mContext).load(mContext.getResources().getDrawable(R.drawable.small_dog)).into(((ToMsgHolder)viewHolder).headImage); if (chatWithFriendBean.getMapUrl() != null && !"".equals(chatWithFriendBean.getMapUrl())) { ((ToMsgHolder)viewHolder).llAddressInfo.setVisibility(View.VISIBLE); ((ToMsgHolder)viewHolder).content.setVisibility(View.GONE); Glide.with(mContext).load(chatWithFriendBean.getMapUrl()).apply(options).into(((ToMsgHolder)viewHolder).address); ((ToMsgHolder)viewHolder).addressContent.setText(chatWithFriendBean.getPoiname() + "\n" + chatWithFriendBean.getLabel()); }else { ((ToMsgHolder)viewHolder).llAddressInfo.setVisibility(View.GONE); ((ToMsgHolder)viewHolder).content.setVisibility(View.VISIBLE); ((ToMsgHolder)viewHolder).content.setText(chatWithFriendBean.getContent()); } } } //定义一个枚举,分别表示接受消息和发送消息 public enum ITEM_TYPE { TYPE_FRIEND_MSG, TYPE_ME_MSG } //根据消息的属性返回不同的viewType @Override public int getItemViewType(int position) { if (mList.get(position).isFriendMsg()) { return ITEM_TYPE.TYPE_FRIEND_MSG.ordinal(); }else { return ITEM_TYPE.TYPE_ME_MSG.ordinal(); } } //刷新消息接口 public void update(List<ChatWithFriendBean> list) { mList = list; notifyDataSetChanged(); } @Override public long getItemId(int position) { return position; } @Override public int getItemCount() { if (mList == null || mList.size() == 0) return 0; return mList.size(); } //接收的消息的viewHolder public static class FromMsgHolder extends RecyclerView.ViewHolder { private ImageView headImage; private TextView content; private ImageView address; private LinearLayout llAddressInfo; private TextView addressContent; public FromMsgHolder(View itemView) { super(itemView); headImage = itemView.findViewById(R.id.friend_avatar); content = itemView.findViewById(R.id.friend_content); address = itemView.findViewById(R.id.friend_address); llAddressInfo = itemView.findViewById(R.id.ll_address_info); addressContent = itemView.findViewById(R.id.address_content); } } //发送的消息的viewHolder public static class ToMsgHolder extends RecyclerView.ViewHolder { private ImageView headImage; private TextView content; private ImageView address; private LinearLayout llAddressInfo; private TextView addressContent; public ToMsgHolder(View itemView) { super(itemView); headImage = itemView.findViewById(R.id.me_avatar); content = itemView.findViewById(R.id.me_content); address = itemView.findViewById(R.id.me_address); llAddressInfo = itemView.findViewById(R.id.ll_address_info); addressContent = itemView.findViewById(R.id.address_content); } } }
上面是Adapter的创建过程,就是通过getItemViewType、onCreateViewHolder和bindViewHolder这几个方法创建不同的viewHolder并且和各自的数据进行绑定的,这样RecyclerView就可以显示不同的Item了。
//创建RecyclerView对象 chatRecyclerView = findViewById(R.id.chatRecyclerView); //创建adapter对象 chatAdapter = new RecycleViewAdapter(this,getData()); //设置布局方式 chatRecyclerView.setLayoutManager(new LinearLayoutManager(this)); //recyclerView和adapter绑定 chatRecyclerView.setAdapter(chatAdapter); //模拟数据 private List<ChatWithFriendBean> getData() { List<ChatWithFriendBean> mList = new ArrayList<>(); ChatWithFriendBean friChatBeanOne = new ChatWithFriendBean(); friChatBeanOne.setContent("你好,小汪"); friChatBeanOne.setFriendMsg(true); mList.add(friChatBeanOne); ChatWithFriendBean meChatBeanOne = new ChatWithFriendBean(); meChatBeanOne.setContent("在的,小喵,干啥"); meChatBeanOne.setFriendMsg(false); mList.add(meChatBeanOne); ChatWithFriendBean friChatBeanTwo = new ChatWithFriendBean(); friChatBeanTwo.setContent("在哪里呢"); friChatBeanTwo.setFriendMsg(true); mList.add(friChatBeanTwo); ChatWithFriendBean meChatBeanTwo = new ChatWithFriendBean(); meChatBeanTwo.setContent("在逛街"); meChatBeanTwo.setFriendMsg(false); mList.add(meChatBeanTwo); ChatWithFriendBean friChatBeanThree = new ChatWithFriendBean(); friChatBeanThree.setContent("在哪里逛街呢,地址给我发一下,去找你"); friChatBeanThree.setFriendMsg(true); mList.add(friChatBeanThree); ChatWithFriendBean meChatBeanThree = new ChatWithFriendBean(); meChatBeanThree.setMapUrl("http://restapi.amap.com/v3/staticmap?markers=mid,0xFF0000,A:116.37359,39.92437&key=352c8f38a730448b4b21d35f355a3bdb"); meChatBeanThree.setPoiname("广济寺"); meChatBeanThree.setLabel("北京城内西城区阜成门内大街25号"); meChatBeanThree.setFriendMsg(false); mList.add(meChatBeanThree); ChatWithFriendBean friChatBeanFour = new ChatWithFriendBean(); friChatBeanFour.setMapUrl("http://restapi.amap.com/v3/staticmap?markers=mid,0xFF0000,A:116.38359,39.62437&key=352c8f38a730448b4b21d35f355a3bdb"); friChatBeanFour.setPoiname("南薰殿"); friChatBeanFour.setLabel("南薰殿位于外朝西路,武英殿西南"); friChatBeanFour.setFriendMsg(true); mList.add(friChatBeanFour); return mList; }
RecyclerView实现聊天界面就是这样做的
最后附上demo代码地址:
https://github.com/xinhuashi/ChatRecyclerView.git