关于BaseAdapter中getView方法的理解与实现

前言

对于刚接触ListView组件的朋友来说,虽然很多教材或者文章都介绍了利用ListView实现列表的一般步骤,大家也都可以依葫芦画瓢去实现简单的列表效果,但同时也会产生些许疑惑,比如何adapter,其作用是什么?实现的getView方法被谁调用,何时被调用等等,这里通过一个小的案例来研究getView方法的调用和传参机制,帮助大家更好的理解和应用。

案例

为了研究getView方法的调用机制,这里首先使用ListView组件完成一个商品列表案例,效果如下:
在这里插入图片描述
Activity源码如下:

public class MainActivity extends AppCompatActivity{
    ListView lv;
    //需要适配的数据
    private String[] titles = { "桌子", "苹果", "蛋糕", "线衣", "猕猴桃", "围巾",
            "桌子", "苹果", "蛋糕", "线衣", "猕猴桃", "围巾"};
    private String[] prices = { "1800元", "10元/kg", "300元", "350元", "10元/kg", "280元",
            "1800元", "10元/kg", "300元", "350元", "10元/kg", "280元"};
    //图片集合
    private int[] icons = {R.drawable.table,R.drawable.apple,R.drawable.cake,
            R.drawable.wireclothes,R.drawable.kiwifruit,R.drawable.scarf,
            R.drawable.table,R.drawable.apple,R.drawable.cake,
            R.drawable.wireclothes,R.drawable.kiwifruit,R.drawable.scarf};
    //保存添加的商品个数
    private int[] no = new int[]{0,0,0,0,0,0,0,0,0,0,0,0};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //全屏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //初始化组件对象
        initView();
        //创建适配对象
        MyAdapter myAdapter = new MyAdapter();
        //绑定到ListView对象
        lv.setAdapter(myAdapter);
    }
    //将xml组件转换成组件对象
    private  void initView(){
        lv = (ListView)findViewById(R.id.lv);
    }
    class MyAdapter extends BaseAdapter{
        class ItemComs{
            ImageView iv;
            TextView tvTop;
            TextView tvBottom;
            TextView tvNo;
            Button btJ;
            Button btZ;
        }
        @Override
        public int getCount() {
            return titles.length;
        }
        @Override
        public Object getItem(int position) {
            return titles[position];
        }
        @Override
        public long getItemId(int position) {
            return position;
        }
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            ItemComs ics = new ItemComs();
            if(convertView == null){
                //Log.e("position",position+"");
                convertView = View.inflate(MainActivity.this,R.layout.item_view,null);
                findViews(ics,convertView);
                convertView.setTag(ics);
            }
            else{
                ics = (ItemComs) convertView.getTag();
            }
            setOnEvents(ics,position);
            initViews(ics,position);
            return convertView;
        }
        //将layouy_item组件转换成对象
        private void findViews(ItemComs ics,View view){
            ics.iv = (ImageView)view.findViewById(R.id.iv);
            ics.tvTop = (TextView) view.findViewById(R.id.tvTop);
            ics.tvBottom = (TextView)view.findViewById(R.id.tvBottom);
            ics.tvNo = (TextView)view.findViewById(R.id.tvNo);
            ics.btJ = (Button)view.findViewById(R.id.btJ);
            ics.btZ = (Button)view.findViewById(R.id.btZ);
        }
        //设置组件内容
        private  void initViews(ItemComs ics,int position){
            ics.iv.setImageResource(icons[position]);
            ics.tvTop.setText(titles[position]);
            ics.tvBottom.setText(prices[position]);
            ics.tvNo.setText(no[position]+"");
        }
        //给“+”“-”按钮绑定事件
        private  void setOnEvents(final ItemComs ics,final int position){

            ics.btJ.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(no[position]<=0){
                        Toast.makeText(MainActivity.this,"请添加商品",Toast.LENGTH_SHORT).show();
                    }
                    else{
                        no[position] -= 1;
                        ics.tvNo.setText(no[position]+"");
                    }
                }
            });
            ics.btZ.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    no[position] += 1;
                    ics.tvNo.setText(no[position]+"");
                }
            });
        }
    }
}

BaseAdapter

BaseAdapter是最万能最好用的数据适配器之一,可以给ListView、Spinner、GridView多种控件填充数据。通俗的说,适配器的作用就是在数据和视图之间建立一种桥梁,类似一个转换器,能够将复杂的数据转换成用户可以接受的方式进行呈现。本案例的核心就是继承BaseAdapter类并实现其4个抽象方法:
1.int getCount() 填充的item个数
2.Object getItem(int position) 指定索引对应的item数据项
3.long getItemId(int position) 指定索引对应item的id值
4.View getView(final int position, View convertView, ViewGroup parent) 填充每个item的可视内容并返回
其中最重要最核心的就是getView方法,它负责给每个item填充内容,并且返回视图对象。这里重点罗列下,初写案例时对getView方法的一些疑惑:

问题

1.getView方法由谁调用,何时被调用?
2.参数positon、convertView 分别代表什么意思?
3.View.inflate静态方法的作用?
4.convertView.setTag();getTag()方法的作用?

实验与理解

为了更清楚的了解运行机制,这里在getView方法中添加一处打印,输出position及convertView 。
在这里插入图片描述
程序运行初始界面及结果:
在这里插入图片描述通过运行结果可以清楚的看到,当运行程序时,由于ListView控件的可视区域内最大能够容纳7个item,所以一共调用了7次getView方法,每次绘制出一个item,并且position代表着每个item的索引号,从0开始。

继续实现,滑动屏幕看效果:
在这里插入图片描述
通过gif图可以很清楚的发现,由于ListView的可视区域是固定的,最多能够显示7个item,因此在滑动屏幕时,肯定会出现某些item被划出屏幕,同时又有item被划入屏幕显示的现象,那么每当某个item进入到可视区域,就会自动调用getView方法来填充数据并绘制。并且convertView这个引用在屏幕滑动过程中不再被赋值了,也就是说除了界面启动时初次绘制ListView的7个item区域时通过View.inflate分别获取到layout_item布局对象后,再也不用被赋值了。
因此得出结论:
1.getView方法是由系统自动回调的方法,每当可视区域内需要刷新一个item时就会被调用,用来填充item内容、绑定事件等其他操作。
2.参数position是系统回调getView方法时自动传入的,代表当前刷新的item的索引号,下标从0开始。
3.界面启动时,自动调用getView方法传入的convertView均为null; 根据代码逻辑
if(convertView == null){ convertView = View.inflate(MainActivity.this,R.layout.item_view,null); findViews(ics,convertView); convertView.setTag(ics); }
当convertView为null时就是调用View.inflate给其赋值。(关于这一块的理解见后文)
4.每当某个item进入到可视区域,就会自动调用getView方法来填充数据并绘制View。
5.convertView的理解,就拿本案例来说,如果把ListView区域理解成一片试验田基地,那么这个基地最大能开发7个试验田。
启动程序——就好像在规划和搭建试验基地,这时候试验田只是规划了区域,还没有正式开开挖。
初始界面——7个item显示完成,就好像完成了7个试验田的挖掘工作,convertView就可以理解成7个试验田的标识牌。因此第一次调用getView方法时convertView都为null;然后通过View.inflate将layout_item转换成View对象,就好比挖掘完成,然后贴上convertView标识牌。所以7块试验田,一共挖掘7次,convertView被赋值7次,分别代表不同区域的itemlayout信息。
界面滚动——完成启动,试验田已经开挖完成,当界面滚动时虽然会有新的item进入旧的item出去,不管你有多少个item但实验田数量依旧是7个,只是填充不同的内容即可,就好像这片田种完花生,然后收获了再种棉花,田还是一片田,因此不用再挖掘(当然你要在挖也行,只是代码反复执行View.inflate降低执行效率),只需将数据填充进layout_item各个控件即可。

结合代码再理解

根据刚才提出的试验田原理,我们结合getView源码进行最后分析:
ListView——实验基地
layout_item——实验田区布局
class ItemComs——实验田区内部控件集合
View.inflate——挖掘实验田,无需重复挖掘
convertView——实验田区标识,保存实验田区布局对象信息,通过该对象可以找到具体实验田,进而通过findViewById找到ItemComs各控件
convertView.setTag(ics);——保存ItemComs对象,方便下次绘制item时直接填充内容。
convertView.getTag(ics);——获取ItemComs对象,绘制item时直接填充内容。
真编不下去了,就这样把,有些用语言很好描述的问题,为啥用文字就这么难!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值