ListView是我们开发中最常用的控件之一,不过现在差不多bei后起之秀RecycleView抢了风头,但是ListView之中的一些思想还是对开发能起大作用的。好的,今天我们要探讨的主题就是ListView中的多布局技术。
这是一个ListView多布局的简单实现(很丑我知道),通过这种方式,能够实现像QQ、微信的聊天界面,也或者是像联系人列表等等。下面说一说实现过程,大致流程和正常使用ListView步骤相差不大,只不过在适配器需要做一些简单的修改。
一、准备数据:创建JavaBean和添加数据
// 1. 准备三个数据源(这是假数据)
List<Object> data = new ArrayList<>();
for (int i = 0; i < 10; i++) {
data.add(new FoodBean("佛跳墙", "1000"));
data.add(new SportBean("篮球", "乒乓球", R.mipmap.sport_img));
data.add(new FruitBean("苹果", "10"));
data.add(new SportBean("跑步", "网球", R.mipmap.sport_img));
}
二、创建适配器:需要重写getItemViewType()和getViewTypeCount(),(涉及到Java关键字instanceof的解释以及setTag()的解释)
public class MyAdapter extends BaseAdapter {
private static final int TYPE_FOOD = 0; // 不同的item布局给不同的标志
private static final int TYPE_FRUIT = 1;
private static final int TYPE_SPORT = 2;
private Context context;
private List<Object> data;
public MyAdapter(Context context, List<Object> data) {
this.context = context;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
// 多布局的核心,通过此方法判断类别
@Override
public int getItemViewType(int position) {
if (data.get(position) instanceof FoodBean) // instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例
return TYPE_FOOD;
else if (data.get(position) instanceof FruitBean)
return TYPE_FRUIT;
else if (data.get(position) instanceof SportBean)
return TYPE_SPORT;
else
return super.getItemViewType(position);
}
// 有几种item,就写几
@Override
public int getViewTypeCount() {
return 3;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position); // 通过索引取对应的item布局的类别
ViewHolder1 holder1 = null; // 初始化ViewHolder
ViewHolder2 holder2 = null;
ViewHolder3 holder3 = null;
if (convertView == null) { // 复用convertView
switch (type) { // 通过item布局的类别去匹配对应的item
case TYPE_FOOD:
convertView = View.inflate(context, R.layout.item1_food, null); // 打气一个布局
holder1 = new ViewHolder1(convertView); // 使用ViewHolder,减少findViewById()的次数
convertView.setTag(holder1); // Tag(SDK给出的解释): Tag不像ID是用标示view的。Tag从本质上来讲是就是相关联的view的额外的信息。它们经常用来存储一些view的数据,这样做非常方便而不用存入另外的单独结构。
/**
* 这里提一下setTag()
* 首先我们要知道setTag方法是干什么的,他是给View对象的一个标签,标签可以是任何内容,
* 我们这里把他设置成了一个对象,因为我们是把item1_food.xml的元素抽象出来成为一个类ViewHolder,
* 用了setTag(),这个标签就是ViewHolder实例化后对象的一个属性。我们之后对于ViewHolder实例化的对象holder的操作,
* 都会因为java的引用机制而一直存活并改变convertView的内容,而不是每次都是去new一个。我们就这样达到的重用
* */
break;
case TYPE_FRUIT:
convertView = View.inflate(context, R.layout.item2_fruit, null);
holder2 = new ViewHolder2(convertView);
convertView.setTag(holder2);
break;
case TYPE_SPORT:
convertView = View.inflate(context, R.layout.item3_sport, null);
holder3 = new ViewHolder3(convertView);
convertView.setTag(holder3);
break;
}
} else {
switch (type) {
case TYPE_FOOD:
holder1 = (ViewHolder1) convertView.getTag(); // 通过convertView上面设置的Tag,得到holder对象,此时holder对象已有对item中的控件的操作权
break;
case TYPE_FRUIT:
holder2 = (ViewHolder2) convertView.getTag();
break;
case TYPE_SPORT:
holder3 = (ViewHolder3) convertView.getTag();
break;
}
}
Object obj = data.get(position); // 通过索引得到对应的实体对象
switch (type) {
case TYPE_FOOD:
FoodBean food = (FoodBean) obj;
if (food != null) { // 如果实体类中对象的值不为空,则更新UI
holder1.item1_tv_foodName.setText(food.getFo());
holder1.item1_et_foodPrice.setText(food.getPrice());
}
break;
case TYPE_FRUIT:
FruitBean fruit = (FruitBean) obj;
if (fruit != null) {
holder2.item2_et_fruitName.setText(fruit.getApple());
holder2.item2_tv_fruitPrice.setText(fruit.getPrice());
}
break;
case TYPE_SPORT:
SportBean sport = (SportBean) obj;
if (sport != null) {
holder3.item3_iv_img.setImageResource(sport.getPhoto());
holder3.item3_tv_type1.setText(sport.getRun());
holder3.item3_tv_type2.setText(sport.getTennis());
}
break;
}
return convertView; // 返回item布局
}
}
最后(MainActivity):
// 3. 给ListView设置适配器(值)
main_lv.setAdapter(adapter);
搞定!
补充:这里我将ViewHolder设置为外部类,方便管理。
如有不同意见,请多指教!