ListView列表项View复用--ViewHolder模式

名词介绍

ListView我们称之为列表,ListView中显示的每列,我们称之为列表项。

内容:

本文讲列表项View复用,那么何时会复用列表项View?
当列表存在滚动时,即所有列表项不能被全部看到,就会复用View。

本文以ListView使用时重写BaseAdapter的适配器为例,讲解列表项View复用中的ViewHolder模式实现的控件对象复用。

在重写BaseAdapter时,通过重写getView方法实现自定义列表项View和列表项View复用,每个列表项的每次展示都需要调用getView方法,getView方法返回值和参数如下。

public View getView(int position, View convertView, ViewGroup parent)

接下来会通过对getView3种不同写法的对比来讲解ViewHolder模式实现的列表项View复用中的控件对象复用。
在此我们将列表项View分为两种,一种是布局复用(即xml文件),一种是控件复用(Button、TextView、ImageView等基本控件),此文例子中的控件是TextView。
依据这两种View,本文中的3种写法分别对应如下:

  1. 不复用布局和控件
  2. 复用布局(即getView方法参数中的View convertView)
  3. 复用布局和控件(即TextView,通过ViewHolder模式实现)

第一种写法,不复用View

class Adapter1 extends BaseAdapter {
    
    
        ArrayList<String> data;
        Context context;

        public Adapter1(ArrayList<String> data, Context context) {
    
    
            this.data = data;
            this.context = context;
        }

        @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 View getView(int position, View convertView, ViewGroup parent) {
    
    
            View view = LayoutInflater.from(context).inflate(R.layout.text_row_item, null);
            TextView tv = view.findViewById(R.id.textView);
            tv.setText(data.get(position));

            return view;
        }


    }

因为在列表滚动时每次看到不同的列表项都会调用getView方法,上面的getView方法在每次调用getView时都会生成一个新的布局View和TextView控件对象,在不断滚动列表时,会不断生成新的布局View和TextView,会使内存持续增加。

第二种写法,复用布局(即getView方法中的View convertView)。

class Adapter2 extends BaseAdapter {
    
    
        ArrayList<String> data;
        Context context;
       //用于查看布局View的个数
        int i = 1;

        public Adapter2(ArrayList<String> data, Context context) {
    
    
            this.data = data;
            this.context = context;
        }

        @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 View getView(int position, View convertView, ViewGroup parent) {
    
    
            if (convertView == null) {
    
    
                convertView = LayoutInflater.from(context).inflate(R.layout.text_row_item, null);
                Log.i("Adapter2","convertView"+i);
                i++;
            }
            TextView tv = convertView.findViewById(R.id.textView);
            tv.setText(data.get(position));

            return convertView;
        }
    }

布局View的复用,是ListView实现的,实现的原理是当列表项View在屏幕中不可见时此列表项View即可被复用,此复用原理不在本文中讲述,通过判断getView方法的View参数是否为空,来判断是否需要新生成布局View,以此方法来复用View,减少布局View对象的生成,减少内存的使用,因生成新的View是IO操作,因此也可以提高访问速度。

第三种写法,复用布局和控件(即TextView,通过ViewHolder模式实现)

class Adapter3 extends BaseAdapter {
    
    

        ArrayList<String> data;
        Context context;
        //用于查看布局View的个数
        int i = 1;

        public Adapter3(ArrayList<String> data, Context context) {
    
    
            this.data = data;
            this.context = context;
        }

        @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 View getView(int position, View convertView, ViewGroup parent) {
    
    
            ViewHolder viewHolder = null;
            if (convertView == null) {
    
    
                convertView = LayoutInflater.from(context).inflate(R.layout.text_row_item, null);
                viewHolder = new ViewHolder();
                viewHolder.textView = (TextView) convertView.findViewById(R.id.textView);
                convertView.setTag(viewHolder);
                Log.i("Adapter3","convertView+TextView"+i);
                i++;
            }else{
    
    
                viewHolder = (ViewHolder) convertView.getTag();
            }
            viewHolder.textView.setText(data.get(position));

            return convertView;
        }

        class ViewHolder{
    
    
            TextView textView;
        }
    }

与第二中写法相比,此写法增加了一个内部类ViewHolder,此类名可自定义,不是必须要写为ViewHolder,ViewHolder类中有一个TextView对象,此类是用于存储布局View中的控件(TextView)的对象的。

在新生成布局View的时候,新建一个ViewHolder对象,然后获取控件(TextView)的对象并将其存储在ViewHolder对象中,然后将ViewHolder对象通过布局View的setTag()方法存储在布局View中。

当复用布局View的时候,不用重新获取控件(TextView)对象,直接把通过布局View的setTag方法存储的ViewHolder对象,通过布局View的getTag方法获取,即可通过ViewHolder对象使用控件(TextView)对象,以此来实现控件(TextView)的复用,不用每次调用getView时都生成新的控件对象,再次减少内存的使用,因获取控件对象操作也是IO操作,也可再次提高访问速度。

第三种写法的实现方式,我们称之为ViewHolder模式。

猜你喜欢

转载自blog.csdn.net/li1500742101/article/details/115802179