即时聊天控件一般采用ListView,因为ListView可以支持不同类型的item,同时ListView本身自己维护了复用机制,可以避免大量的item重复创建导致OOM,但是由于ListView复用机制所以对于编写适配器就需要避免界面混乱,在第一次做布局的时候,一个播放语音动画就导致了布局了混乱显示,所以要编写一个好的apdater去深入了解复用机制去避免界面混乱.
首先我们需要创建一个布局,布局包含了ListView,用来显示主要的对话界面:
<RelativeLayout 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"> <!--输入界面--> <include android:id="@+id/ll_input" layout="@layout/chat_input" /> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/ll_input" android:divider="@null" /> </RelativeLayout>
然后我们需要编写一个Adapter去设置给ListView,那么我们就要考虑用什么apdater.我们都知道SimpleAdapter支持:CheckBox,RadioButton,TextView,ImageView.而我们的需求是要编写复杂的Item,所以我们应该选择BaseAdapter.
在BaseAdapter中我们需要重写的方法:
@Override public int getCount() { return messages.size(); }
@Override public Message getItem(int position) { return messages.get(position); }
@Override public long getItemId(int position) { return position; }
@Override public View getView(int position, View convertView, ViewGroup parent) { return convertView; }上面仅仅是简述这几个基本方法,这几个基本的方法仅仅是适用于一个不变的Item,上面是一个不变的Item,? 就是单一元素的,样式大小控件个数等.为什么.?因为系统在缓存池拿去复用的控件的时候就会去判断是否单一,默认为单一,那么拿到的控件与新的Item样式大小和控件个数都不同,那么给当前的控件设置一些属性就会NullPiontException等.这也是关键所在.
所以我们还需要根据自己的需求重写2个方法,分别为:
@Override public int getItemViewType(int position) { Message message = messages.get(position); MessageContent messageContent = message.getContent(); if (messageContent instanceof TextMessage) {// 文本 return message.getMessageDirection() == Message.MessageDirection.RECEIVE ? MESSAGE_TYPE_RECV_TXT : MESSAGE_TYPE_SENT_TXT; } else if (messageContent instanceof ImageMessage) { // 图片 return message.getMessageDirection() == Message.MessageDirection.RECEIVE ? MESSAGE_TYPE_RECV_IMAGE : MESSAGE_TYPE_SENT_IMAGE; } else if (messageContent instanceof LocationMessage) {// 位置 return message.getMessageDirection() == Message.MessageDirection.RECEIVE ? MESSAGE_TYPE_RECV_LOCATION : MESSAGE_TYPE_SENT_LOCATION; } else if (messageContent instanceof RichContentMessage) {// 图文消息 return message.getMessageDirection() == Message.MessageDirection.RECEIVE ? MESSAGE_TYPE_RECV_RICH_CONTENT : MESSAGE_TYPE_SENT_RICH_CONTENT; } else if (messageContent instanceof VoiceMessage) { // 语音消息 return message.getMessageDirection() == Message.MessageDirection.RECEIVE ? MESSAGE_TYPE_RECV_VOICE : MESSAGE_TYPE_SENT_VOICE; } else if (messageContent instanceof InformationNotificationMessage) {//灰色提示消息 return MESSAGE_TYPE_RECV_NTF_MSG; } return -1; }
@Override public int getViewTypeCount() { return 12;//暂时12种,后续根据业务会增加红包,广告等消息. }
做完这些后我们需要真正的就是去关键的方法getView中了.在getView中我们需要使用google推荐的写法,牺牲空间去换时间,为什么要去牺牲空间换时间.?虽然ListView提供了复用机制,但是每次复用的时候你还是需要去findViewByID,而id本身就是一个二叉树,一级级的遍历非常消耗时间,所以我们还需要增加用户体验,就需要创建一个Holder,避免重复查找.
那么Holder的代码如下:
private class ViewHoder { TextView name;//名字 ImageView head;//头像 TextView content;//文本内容 ProgressBar progressBar;//进度 ImageView state;//状态 TextView time;//消息时间 ImageView imageView;//图片消息 TextView voiceTime;//语音时间 ImageView voiceIcon;//语音图片 LinearLayout voiceGroup;//包裹语音 LinearLayout location;//包裹地图 }
这样getView的完整代码:
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHoder viewHoder = null; final Message message = getItem(position); MessageContent messageContent = message.getContent(); if (null == convertView) { viewHoder = new ViewHoder(); convertView = createViewByMessage(messages.get(position)); if (message.getContent() instanceof TextMessage) { try { viewHoder.time = (TextView) convertView.findViewById(R.id.chat_tv_time); viewHoder.name = (TextView) convertView.findViewById(R.id.chat_tv_name); viewHoder.head = (ImageView) convertView.findViewById(R.id.chat_iv_head); viewHoder.content = (TextView) convertView.findViewById(R.id.chat_tv_content); viewHoder.progressBar = (ProgressBar) convertView.findViewById(R.id.chat_pb); viewHoder.state = (ImageView) convertView.findViewById(R.id.chat_iv_state); } catch (Exception e) { } } else if (message.getContent() instanceof ImageMessage) { try { viewHoder.time = (TextView) convertView.findViewById(R.id.chat_tv_time); viewHoder.name = (TextView) convertView.findViewById(R.id.chat_tv_name); viewHoder.head = (ImageView) convertView.findViewById(R.id.chat_iv_head); viewHoder.imageView = (ImageView) convertView.findViewById(R.id.chat_iv_content); viewHoder.progressBar = (ProgressBar) convertView.findViewById(R.id.chat_pb); viewHoder.state = (ImageView) convertView.findViewById(R.id.chat_iv_state); } catch (Exception e) { } } else if (message.getContent() instanceof VoiceMessage) { try { viewHoder.time = (TextView) convertView.findViewById(R.id.chat_tv_time); viewHoder.name = (TextView) convertView.findViewById(R.id.chat_tv_name); viewHoder.head = (ImageView) convertView.findViewById(R.id.chat_iv_head); viewHoder.voiceTime = (TextView) convertView.findViewById(R.id.chat_tv_voice_time); viewHoder.voiceIcon = (ImageView) convertView.findViewById(R.id.chat_iv_voice); viewHoder.voiceGroup = (LinearLayout) convertView.findViewById(R.id.chat_ll_content); viewHoder.progressBar = (ProgressBar) convertView.findViewById(R.id.chat_pb); viewHoder.state = (ImageView) convertView.findViewById(R.id.chat_iv_state); } catch (Exception e) { } } else if (message.getContent() instanceof LocationMessage) { try { viewHoder.time = (TextView) convertView.findViewById(R.id.chat_tv_time); viewHoder.name = (TextView) convertView.findViewById(R.id.chat_tv_name); viewHoder.head = (ImageView) convertView.findViewById(R.id.chat_iv_head); viewHoder.content = (TextView) convertView.findViewById(R.id.chat_tv_content);// 位置 viewHoder.location = (LinearLayout) convertView.findViewById(R.id.chat_ll_content); viewHoder.progressBar = (ProgressBar) convertView.findViewById(R.id.chat_pb); viewHoder.state = (ImageView) convertView.findViewById(R.id.chat_iv_state); } catch (Exception e) { } } convertView.setTag(viewHoder); } else { viewHoder = (ViewHoder) convertView.getTag(); } handleTime(message, viewHoder, position); //handler UserName and UserHead. if (messageContent instanceof TextMessage) { // 文本消息 handleTextMessage(message, viewHoder, position); } else if (messageContent instanceof ImageMessage) {// 图片消息 handleImageMessage(message, viewHoder, position); } else if (messageContent instanceof VoiceMessage) { // 语音消息 handleVoiceMessage(message, viewHoder, position); } else if (messageContent instanceof RichContentMessage) { // 图文消息 handleRichContentMessage(message, viewHoder, position); } else if (messageContent instanceof InformationNotificationMessage) { // 灰色提示 handleNotifiMessage(message, viewHoder, position); } else if (messageContent instanceof LocationMessage) { // 位置消息 handleLocationMessage(message, viewHoder, position); } else { //not supported. } return convertView; }