Iwfu-ListView(二) -BaseAdapter分析以及实现同一个ListView加载不同的布局

承接上文:
ListView(一) - ListView使用ViewHolder优化以及ListView的其他

上文使用viewHolder对ListView进行优化,防止每次加载视图调用findViewById()。为什么viewHolder要写成static的啊??参考stackOverflow的一个回答:

这里写图片描述

大致是说使用static的好处:

  • 防止非静态内部类的实例包含外围类实例的引用导致的内存泄漏
  • 使用内部类可以让其他的adapter复用,不过不推荐,一般一个adapter写一个viewholder
  • 使用静态类在类加载时就会被调用,比不使用static占用更少的内存

    关于这个问题有更好的答案的请大声举手O(≧口≦)O!!

ListView第二波:BaseAdapter分析

(以下分析纯属个人意见,有错误请指正)
使用ListView最常用的是使用自定义适配器继承BaseAdapter,而BaseAdapter中有很多方法要重写,这里试下创建ListView,使用BaseAdapter加载数据的时候有哪些方法会被调用:
(代码就是最简单的使用viewHolder的自定义适配器,让重写的四个方法每个都打印方法名,这里就不贴了)

直接上结果:

这里写图片描述

这个结果是加载完listview的第一屏(listview只显示了最开始状态的列表项,没有滑动)的输出结果,可以看到重写的四个方法只有getCount()和getView()被调用了,而且在listview多次绘制过程中当getCount调用后才会执行getView(),并每次getView()是根据第一屏能显示的条目数来决定执行多少次,另外BaseAdapter还有两个方法值得留意:getItemViewType()以及getViewTypeCount(),我们重写这两个方法并打印输出:

这里写图片描述

同样看到加载视图时,先执行getCount(),然后执行了getViewTypeCount(),与上面不同的是,这里调用了getItemId(),并且每次调用getView()之后会调用getItemViewType(),不过反复试验后,发现getItemViewType()和getView没有绝对的调用先后顺序,而getCount总是先执行(大家可以自己试验一下),getViewTypeCount总是先于getItemViewType执行。通过查阅Api,可以解释出现这个现象的原因:

  • getCount用于获取listView要加载的数据条目的数量,如果返回0则不执行getView.
  • getViewType 返回的是getView()方法会创建的视图的类型数,也就是当前这个adapter会在listview创建多少种视图,如果整个listView都创建同一种样式的item,则这个方法返回1。
  • getItemViewType返回的是某个特定posititon的视图类型(用int标识的),大小一定在0~ViewType数量-1之间

根据以上的知识,我们就可以做出同一个ListView中显示不同的布局的效果。

同一个ListView加载不同布局:

现在我们来实现这个需求:ListView包含四种布局,上半部分提示信息一行数据+上半部分数据+下半部分提示信息数据+下半部分数据。一共四种类型的布局

关键在重写getViewType()和getItemViewType(),并在getView()中根据类型值的不同对应加载不同的布局。

布局文件就加了一个ListView

主要代码:

“`
package com.chan.betterlistview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class ActivityListView3 extends AppCompatActivity {

private ListView mlistView;
// 要显示的两组数据
private List<String> datasList1;
private List<String> datasList2;
private static final String TAG = "tag";

// 4种类型的标示
private static final int TYPE1 = 0;
private static final int TYPE2 = 1;
private static final int TYPE3 = 2;
private static final int TYPE4 = 3;

private LayoutInflater mLayoutInflater;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_activity_list_view3);
    mLayoutInflater = LayoutInflater.from(this);
    initDataList();
    initListView();
}

private void initDataList() {
    datasList1 = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        datasList1.add(i + "");
    }

    datasList2 = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        datasList2.add(i + 100 + "");
    }
}

private void initListView() {
    mlistView = (ListView) findViewById(R.id.lvlvlvl);
    MyAdapter adapter = new MyAdapter();
    mlistView.setAdapter(adapter);
}

private class MyAdapter extends BaseAdapter {

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            // 第一项显示上半部分提示布局
            return TYPE1;
        } else if (position <= datasList1.size()) {
            // 接下来显示上半部分数据布局
            return TYPE2;
        } else if (position == datasList1.size()+1) {
            // 注意position变化,这里显示下半部分提示布局
            return TYPE3;
        } else {
            // 最后显示下半部分数据布局
            return TYPE4;
        }
    }

    @Override
    public int getViewTypeCount() {
        // 总共四种布局
        return 4;
    }

    @Override
    public int getCount() {
        // list大小变为两种数据集合大小+两个信息提示
        return datasList1.size() + datasList2.size() + 1 + 1;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder1 viewHolder1 = null;
        ViewHolder2 viewHolder2 = null;
        ViewHolder3 viewHolder3 = null;
        ViewHolder4 viewHolder4 = null;

        // 获取当前要加载的是哪一种布局
        int type = getItemViewType(position);

        if (convertView == null) {

            switch (type) {
                case TYPE1 :
                    viewHolder1 = new ViewHolder1();
                    convertView = mLayoutInflater.inflate(
                            R.layout.holder1_layout, parent, false);
                    viewHolder1.iv1 = (ImageView) convertView
                            .findViewById(R.id.holder1_iv);
                    viewHolder1.tv1 = (TextView) convertView
                            .findViewById(R.id.holder1_tv);
                    convertView.setTag(viewHolder1);
                    break;
                case TYPE2 :
                    viewHolder2 = new ViewHolder2();
                    convertView = mLayoutInflater.inflate(
                            R.layout.holder2_layout, parent, false);
                    viewHolder2.tv2 = (TextView) convertView
                            .findViewById(R.id.holder2_tv);
                    convertView.setTag(viewHolder2);
                    break;
                case TYPE3 :
                    viewHolder3 = new ViewHolder3();
                    convertView = mLayoutInflater.inflate(
                            R.layout.holder3_layout, parent, false);
                    viewHolder3.iv3 = (ImageView) convertView
                            .findViewById(R.id.holder3_iv);
                    viewHolder3.tv3 = (TextView) convertView
                            .findViewById(R.id.holder3_tv);
                    convertView.setTag(viewHolder3);
                    break;
                case TYPE4 :
                    viewHolder4 = new ViewHolder4();
                    convertView = mLayoutInflater.inflate(
                            R.layout.holder4_layout, parent, false);
                    viewHolder4.tv4 = (TextView) convertView
                            .findViewById(R.id.holder4_tv);
                    convertView.setTag(viewHolder4);
                    break;
                default :
                    break;
            }

        } else {
            switch (type) {
                case TYPE1 :
                    viewHolder1 = (ViewHolder1) convertView.getTag();
                    break;
                case TYPE2 :
                    viewHolder2 = (ViewHolder2) convertView.getTag();
                    break;
                case TYPE3 :
                    viewHolder3 = (ViewHolder3) convertView.getTag();
                    break;
                case TYPE4 :
                    viewHolder4 = (ViewHolder4) convertView.getTag();
                    break;
                default :
                    break;
            }
        }

        // 设置具体的数据,注意取值的时候position的变化
        switch (type) {
            case TYPE1 :
                viewHolder1.iv1.setImageResource(R.mipmap.ic_launcher);
                viewHolder1.tv1.setText("上半部分的数据有:" + datasList1.size() + "个");
                break;
            case TYPE2 :
                viewHolder2.tv2.setText(datasList1.get(position-1));
                break;
            case TYPE3 :
                viewHolder3.iv3.setImageResource(R.mipmap.ic_launcher);
                viewHolder3.tv3.setText("下半部分的数据有:" + datasList2.size() + "个");
                break;
            case TYPE4 :
                viewHolder4.tv4.setText(datasList2.get(position-2-datasList1.size ()));
                break;
            default :
                break;
        }

        return convertView;
    }
}

// 定义出不同的viewHolder对应缓存不同的布局
static class ViewHolder1 {
    ImageView iv1;
    TextView tv1;
}
static class ViewHolder2 {
    TextView tv2;
}
static class ViewHolder3 {
    ImageView iv3;
    TextView tv3;
}
static class ViewHolder4 {
    TextView tv4;
}

}

最后实现的效果:

“`这里写图片描述

对这个例子的几点说明:

  • 这个例子可以在同一个listView加载不同布局,但方法并不唯一。

  • 代码未经封装显得杂乱,但内部逻辑很简单,就是通过布局类型来判断getView()应该加载哪一种布局,填充数据时也根据类型判断应该填入哪些数据,实际使用中封装后会更好。

  • position的位置要进行多次判断,同时因为涉及到许多布局,他们对应的数据集合有不同的大小,所以要格外仔细计算position的位置,否则会导致程序崩溃。

            精力有限,就此搁笔,不足之处,敬请指教。
    

下一篇:实现ListView万能适配器(周日完成)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值