在我们的项目中,并不是所有列表都是简单的使用。类似于新闻列表、QQ聊天列表等,具有图文并排的列表,每个item都有它不同的布局类型,都有其不同的实现方式。这类复杂的列表表现形式,在各类知名应用当中,不可或缺的存在着。今天我们就来看看,使用ListView能不能实现此类复杂布局。
今天的实例为众所周知的QQ聊天界面:
一、纵观全局
1、一个ListView。
2、圆形头像和消息框。
3、时间分隔行。
二、技术选型
1、列表当然选择ListView,我们今天使用的就是这个。
2、圆形头像,目前三方开源的比较多,有CircleImageView、AvatarImageView等可以选择,今天我就选择AvatarImageView。为什么要选择这个,它是我常用的控件之一,只是最近使用它的频率比较高,就选择它了。后续我会有专门贴子说明圆形头像控件的选择。
3、文本框+layout组合成消息框。文本框显示消息内容,layout显示消息背景泡泡。
4、item的实现有几种方法:
1)写一个布局xml文件。里面include三个xml布局,一是自己发送消息,消息显示在右边的布局。二是对方发消息,消息显示在界面左边的布局。三是时间布局,显示在中间,并起到分隔作用。然后根据不同的消息类型,控制布局的显示与隐藏。
2)根据数据时间变化,以及不同消息的类型。直接构造不同的布局文件。这样就比较轻量级。不会在同一个布局当中,存在许多无用的布局。
3)在此我选择第一种方式,因为ListView有所限制,使用第二种方式不是特别方便。第二种方式我会使用后续的RecyclerView来讲解。
三、码代码
1、在上一篇listview简单用法当中,我们使用的viewholder,是集成在adapter当中进行优化的。另外,在这里我引入另外一个解耦合比较高的ViewHolder的写法。
另外新建一个专门的ViewHolder类,在里面实现静态的getViewHolder方法,比较简单,只是将内容移了个地方而已,直接看代码:
package oliver.zhantao.oliverproject.listviewhigh;
import android.view.View;
import android.view.ViewStub;
import android.widget.TextView;
import cn.carbs.android.avatarimageview.library.AvatarImageView;
import oliver.zhantao.oliverproject.R;
/**
* ViewHolder的抽离,单独形成一个类,可以让代码更加明了
* 另外,也可以使adapter更加轻量级,降低耦合性。
* <p>
* 将ViewHolder的实现方法完全封装在其本身的类中,功能更强大
* 其方法与Adapter的getView()方法隔离,条理更加清晰,降低耦合性
* 当getView()中需要实现多种样式时,不需要写重复代码
* <p>
* Created by ZhanTao on 2017/4/19.
*/
public class HighViewHolder {
public View mineView;
public View otherView;
public View timeView;
public AvatarImageView mineView_head;
public AvatarImageView otherView_head;
public TextView mineView_message;
public TextView otherView_message;
public TextView timeView_time;
// 构造函数中就初始化View
public HighViewHolder(View convertView) {
mineView = convertView.findViewById(R.id.view_mine);
otherView = convertView.findViewById(R.id.view_other);
timeView = convertView.findViewById(R.id.view_time);
mineView_head = (AvatarImageView) mineView.findViewById(R.id.aiv_head);
otherView_head = (AvatarImageView) otherView.findViewById(R.id.aiv_head);
mineView_message = (TextView) mineView.findViewById(R.id.tv_message);
otherView_message = (TextView) otherView.findViewById(R.id.tv_message);
timeView_time = (TextView) timeView.findViewById(R.id.tv_time);
}
// 得到一个ViewHolder
public static HighViewHolder getViewHolder(View convertView) {
HighViewHolder viewHolder = (HighViewHolder) convertView.getTag();
if (viewHolder == null) {
viewHolder = new HighViewHolder(convertView);
convertView.setTag(viewHolder);
}
return viewHolder;
}
}
2、再来看我们的adapter,在这个adapter当中,直接通过静态方法就可以获取ViewHolder实例,是不是简单明了多了。注意看getView方法。
package oliver.zhantao.oliverproject.listviewhigh;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.List;
import oliver.zhantao.oliverproject.R;
/**
* 聊天列表适配器,在这里将数据与控件进行绑定
* <p>
* 项目中都是自定义适配器,很少用到系统提供的简易适配器。 所以果断抛弃系统的简易适配器,自定义想咋样就咋样,一个字,爽^_^
* <p>
* Created by ZhanTao on 2017/4/17.
*/
public class ListViewHighAdapter extends BaseAdapter {
private Context mContext;
// 餐厅列表
private List<MessageBean> messageList;
private LayoutInflater mInflater;
public ListViewHighAdapter(Context context, List<MessageBean> messageList) {
this.mContext = context;
this.messageList = messageList;
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return messageList.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getViewTypeCount() {
// 已经知道有三种布局了,直接写死。当然也可以做成一个可扩展的,项目中灵活性更强。
return 3;
}
@Override
public int getItemViewType(int position) {
return messageList.get(position).getLayoutType();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
HighViewHolder holder;
// 1、优化框架搭起
if (convertView == null) {
convertView = mInflater.inflate(R.layout.activity_listview_high_item, null);
}
//这样使用Holder,简单明了,还不占用adapter
holder = HighViewHolder.getViewHolder(convertView);
// 2、绑定数据到控件
bindViewData(position, convertView, holder);
// 3、控制VIEW的显示与隐藏
controlViewVisible(position, holder);
// 4、绑定监听事件
bindViewClickListene