ListView实现多布局(内含对instance of和setTag()的讲解)

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设置为外部类,方便管理。

 

如有不同意见,请多指教!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值