listVeiw之adapter使用优化及item选中状态的处理

要给listView设置列表数据需要掌握adapter的使用,这也是最基本的用法,初次之外的好多面试中曾被问到这样的问题:你可以说说listView的优化吗?相信不少被问到,今天我们就来解决下这个问题;好了直接上代码:

继承BaseAdapter来使用ListView

以下是继承之后需要实现的方法,我们来具体看看每个方法的作用

  • getCount() 用来获取数据源中的数据对象个数
@Override
    public int getCount() {
        return mData.size();
    }
  • getItem(int position) 用来获取指定位置处的数据源对象
@Override
    public Object getItem(int position) {
        return mData.get(position);
    }
  • getItemId(int position) 获取数据源对象的Id,如果有的话如果数据源对象自己没有定义Id,则可以简单地返回其在数据源中的位置
@Override
    public long getItemId(int position) {
        return position;
    }
  • getView(int position, View convertView, ViewGroup parent) 每当Android ListView需要显示一行时,它会调用此方法BaseAdapter是个抽象类,继承它能很方便的来使用ListView。要覆写四个方法。前三个都很简单。重点是getView方法,这直接决定你的屏幕加载是否顺滑。如果convertView为空,加载布局创建View若不空,则直接通过position,获取list中的item,直接替换view中的内容。
@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d(TAG, "显示:" + position + "行,调用getView()" + "参数convertView==null?" + (convertView == null));
        View rootView = null;
        //如果没有可以重用的控件
        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(mContext);
            rootView = inflater.inflate(R.layout.my_list_item, parent, false); //加载布局,创建View
            rootView.setTag(position);
            Log.d(TAG, "实例化rootView,保存到Tag中的值为:" + position);
            counter++;
            Log.d(TAG, "控件实例化个数:" + counter);
        } else {
            //控件己经被创建过,直接重用
            rootView = convertView;
        }
        //依据位置提取相应的数据源对象
        MyDataClass item = mData.get(position);
        //获取用于显示内容的控件的引用
        TextView titleTextView = (TextView) rootView.findViewById(R.id.tvTitle);
        TextView detailTextView = (TextView) rootView.findViewById(R.id.tvDetail);
        //设置显示内容
        titleTextView.setText(item.getTitle());
        detailTextView.setText(item.getDetail() + " 本行Tag中保存的值为:" + rootView.getTag());
        return rootView;
    }
  • 如果你要加载多种布局那么首先你一定重写这两个方法
    • getItemViewType(int position) 获取某个position位置的布局格式

@Override
public int getItemViewType(int position) {
    if(position==2){
    return 2;
    }
    return 1;
    //根据具体情况来判断
}
  • getViewTypeCount() 这里来获取布局的种类有多少种
@Override
public int getViewTypeCount() {
    return 2;
}
本文看到这里,感觉没有重点,如果是这样,那么这篇文章就等于白写了,实际运用中,我们最关键的就是用到getView()的优化来使得listView的item能够重复利用资源,从而大大的加快了加载的速度,体验更好,当然实际运用中可能你会遇到以下的难点,以下都是实际项目中血淋林的教训,在此罗列出来,下篇有专门写的demo分析这些问题

1.当listView的item中有选中状态效果,比如用到最多的地方,像购物车的例子这样,有选中的判断,如果状态复用的话选中就会错乱,针对这样的问题该怎么解决;
2.再次讲讲不同布局在listview中是怎么使用的;
3.当每个item需要添加实时刷新的数据的时候,设置点击的时候会失去状态,这是为甚么,该怎么解决;
4.以上几个都有分开写的demo,最后还有一个综合的listView写的购物车例子,之后与大家分享。

接下来我们就开始在实战中学习战争

1.分析需求

不只是一个单纯的列表(里面有点击选中的按钮,所以一般的复用模式得适当改变),如下图就是实现的效果:

效果图

上图,我们实现的效果就是实现listview中的两种不同布局的填充,其次就是当列表中有选中状态的时候如何避免滑动时的状态复用,针对这两个问题,我们来看代码:

 @Override
    public int getItemViewType(int position) {
        if (getItemId(position) == 3 || getItemId(position) == 10) {
            return 1;
        }
        return 0;
    }

    @Override
    public int getViewTypeCount() {
        return 2;
    }
  • 在Adapter中要实现两种甚至多种布局需要重写以上两个方法,前面已经详细的介绍过这两个方法的用途,在这里我只是做了简单的处理,针对不同的情况做法当然不一样。==getItemViewType(int position)== 当item的位置为3或者是10的时候,给出的布局格式是1,否则的话给的是0。==getViewTypeCount()== 此方法返回这个列表中需要几种类型的布局,此处返回两种布局类型,当然要在==getView()== 中做处理,继续来看以下的代码:
@Override
    public View getView(final int i, View view, ViewGroup viewGroup) {
        final ViewHolder viewHolder;
        if (view == null) {
            viewHolder = new ViewHolder();
            if (getItemViewType(i) == 0)  {
                view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.iten_view, null);
            } else {
                view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_teo, null);
            }
            viewHolder.cbbox = (CheckBox) view.findViewById(R.id.cb_my);
            viewHolder.tvMy = (TextView) view.findViewById(R.id.tv_coments);
            view.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) view.getTag();
        }

        return view;
        private class ViewHolder {
        public CheckBox cbbox;
        public TextView tvMy;
    }
  • 其中我们在getView方法中做了布局的复用,首先我们判断view是否为空,其次就是ViewHolder的使用,其实就是一个持有者,不需要每次加载的时候都初始化控件,大大的加快了速度,其中我们调用判断getItemViewType(i)==0时,我们初始化一种布局,如果这个位置上的view类型等于1的话就初始化另一种布局,当然给不同布局界面填充数据的时候,也要做不同的判断,针对不同布局的界面也是不一样的,代码就不写了,自己领悟下;不出问题,界面的多布局问题,我们就解决了,接着来看我们复用导致的选中状态错乱的问题

选中状态错乱的问题
* 首先导致选中错乱的原因

在getView方法中做了布局的复用,导致除了布局复用之外,选中的状态也复用了,这样,解决这个问题的方式有好几种吧,不过我只讲一种,因为我只会一种,好吧,我们继续,首先,我们要给每个item设置一个状态:

    private List<Boolean> booleenList;
    //声明一个boolean类型的集合

    ~~~

    //初始化数据
    private void initData() {
        lvtext = (ListView) this.findViewById(R.id.lv_lv);
        myAdapter = new MyAdapter();
        stringList = new ArrayList<>();
        booleenList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            stringList.add("im is" + i + "name");
            booleenList.add(false);
            //初始化数据的时候每个数据加个false的状态
        }
    }

    ~~~

    myAdapter.setData(stringList);
    myAdapter.setBoolData(booleenList);
    //这个步骤很明显就是传值

接下来看我在adapter中是怎么处理的

  @Override
    public View getView(final int i, View view, ViewGroup viewGroup) {
        final ViewHolder viewHolder;
        if (view == null) {
            viewHolder = new ViewHolder();
            if (getItemViewType(i) == 0) {
                view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.iten_view, null);
            } else {
                view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_teo, null);
            }
            viewHolder.cbbox = (CheckBox) view.findViewById(R.id.cb_my);
            viewHolder.tvMy = (TextView) view.findViewById(R.id.tv_coments);
            view.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.tvMy.setText(data.get(i));
        //注意以下这行代码
        viewHolder.cbbox.setChecked(boolData.get(i));
        viewHolder.cbbox.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //这里直接调用方法
                //MainActivity.selectChange(i, boolData);
                //自定义的接口
                slecct.seleckt(i, boolData);
            }
        });
        return view;
    }
  • 以上代码三个地方有备注,全是有关于处理选中状态丢失的,先看第一个 ==viewHolder.cbbox.setChecked(boolData.get(i));== 这里我们根据传过来的boolean数据来做出判断选择处理,如果这个位置上的boolData是true的时候就选中,否则的话就不选,多加了一个判断,至于怎么让集合的某个选中的变成true,就要在接下来的点击事件中做处理,==MainActivity.selectChange(i, boolData);==这里我们调用了activity中的方法来处理的,继续看以下代码:
//可以直接使用
    public static void selectChange(int i, List<Boolean> boolData) {
        if (boolData.get(i) == true) {
            boolData.set(i, false);
            myAdapter.notifyDataSetChanged();
        } else if (boolData.get(i) == false) {
            boolData.set(i, true);
            myAdapter.notifyDataSetChanged();
        }
    }


  • 很简单的逻辑就是在点击之后,如果这个位置是true就变换成false,如果是false就变换成true,然后刷新列表,这里写了一个静态类,可以直接调用就可;

以上的代码,暂且到这里好了,这些代码比较简单,我自己有写具体的demo,但是没有上传到github,因为这代码量确实少,还希望自己看着贴出的代码了解了解吧!,下一篇的时候,我们继续讲讲listview的item的点击事件的一些问题吧,实际开发中遇到

想交流,学习,提意见的可以关注以下的二维码:

公众号:君不见And

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值