android app -- 关于listview的几种用法(复用,不复用,半复用)解决item状态改变的问题


1.从最基本的不复用开始,也就是在Adapter 的getView方法中不使用ViewHolder。

这样做,也就是在listView上下滑动,被隐藏的项滑出来的时候,每次都重绘一次这一项,这样的话会耗内存,如果item的数据量比较大的话很有可能出现滑动卡顿的现象。明显的卡顿是开发者最不愿意看到的,所以这样的使用方式,基本被pass掉了。


2.让每一项进行复用,也就是增加ViewHolder 到Adapter的getView中去。具体看代码

package com.tdotapp.rd.adapters;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.tdotapp.rd.R;

import java.util.List;

/**
 * Created by GT on 2015/11/16.
 */
public class typeAdapter extends BaseAdapter {
    //上下文
    private Context mContext;
    //item 中绑的数据
    private List listBeans;
    //构造方法
    public typeAdapter(Context context, List listBeans) {
        this.mContext = context;
        this.listBeans = listBeans;
    }

    @Override
    public int getCount() {
        return listBeans.size();
    }

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //ViewHolder
        ViewHolder viewHolder=null;
        //绘制每一项的时候先判断convertView是否为空,不为空,则else里面去复用,为空,则重新赋予item布局
        if (convertView == null) {
            //new出ViewHolder ,初始化布局文件
            viewHolder = new ViewHolder();
            convertView = LayoutInflater.from(mContext).inflate(R.layout.type_spinner_item, null);
            viewHolder.tvfabuwz = (TextView) convertView.findViewById(R.id.tvfabuwz);
            //调用convertView的setTag方法,将viewHolder放入进去,用于下次复用
            convertView.setTag(viewHolder);
        } else {
            //复用已经存在的item的项
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.tvfabuwz.setText(listBeans.get(position));
        return convertView;
    }

    class ViewHolder {
        TextView tvfabuwz;

    }
}
代码上增加了详细的注释,复用绝对是listView最优的优化方案,不用每次都绘制新的item项,创建一次,之后就拿来复用界面,这样一来,再多的项也不用卡耗内存了。原理相当于在上滑和下滑的时候,界面还是原来的,只是把数据换了一下。


复用有复用的好处,当然也有坏处。使用复用的时候,如果每一项里面只是展示数据,复用绝对好使,但是如果listView中的每一项都能操作(比如有个editText,输入内容,上下滑动之后,看输入框里面的内容是不是要被改变,或者在下面复用的时候出现了这个输入框里面的内容。比如,一个单选框,这一项选中之后,上下滑动之后,看这个选中状态是不是乱跑了,之前的选中状态是不是消失了。。。。。等等等,凡是与item项状态有关的,复用的时候肯定就出问题了)


怎么解决呢,这个时候你想到的肯定是不复用吧,不复用就不会出现状态改变的情况了,但是你要想清楚,不复用的话,或许滑动一会,你的这个listView就会因为不停重绘item界面而内存消耗过大,出现滑不动的情况。

下面就来说下如何避免这样的情况出现,即保证滑动之后item项修改的状态还在,又保证不会过多的重绘item使内存消耗多大。

本方法出自 xiaanming大牛的博客(稍做改动)

public class NewsAdapter extends BaseAdapter {

    // 定义Context
    private Context mContext;
    List list = new ArrayList<>();
    //定义hashMap 用来存放之前创建的每一项item
    HashMap, View> lmap = new HashMap, View>();
    private ImageLoader imageLoader = ImageLoader.getInstance();
    public NewsAdapter(Context context, List listViewList) {
        this.mContext = context;
        this.list = listViewList;

    }

    @Override
    public int getCount() {
        return list.size();
    }

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

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

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder ViewHolder;
        //创建每一个滑动出来的item项,将创建出来的项,放入数组中,为下次复用使用
        if (lmap.get(position) == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.news_item, null);
            ViewHolder = new ViewHolder(convertView);
            convertView.setTag(ViewHolder);
            lmap.put(position,convertView);
        } else {
            convertView = lmap.get(position);
            ViewHolder = (ViewHolder) convertView.getTag();
        }
    ViewHolder.tv_newstime.setText(list.get(position).getDate());
    ViewHolder.tvnewsneirong.setText(list.get(position).getContent());
    ViewHolder.tvnewstitle.setText(list.get(position).getTitle());
    imageLoader.displayImage(list.get(position).getImg(), ViewHolder.imgnewspic, Tools.setBeforImageoption());
    return convertView;
}

//添加viewHolder
class ViewHolder {
    TextView tvnewstitle,newstype,tvnewsneirong,tv_newstime;
    ImageView imgnewspic;

    public ViewHolder(View convertView) {
        tvnewstitle = (TextView) convertView.findViewById(R.id.tvnewtitle);
        imgnewspic = (ImageView) convertView.findViewById(R.id.imgnewspic);
        newstype= (TextView) convertView.findViewById(R.id.newstype);
        tvnewsneirong= (TextView) convertView.findViewById(R.id.tvnewsneirong);
        tv_newstime= (TextView) convertView.findViewById(R.id.tv_newstime);
    }
}

原理就是,有新的界面了,重绘,已经存在的界面直接拿出来。这样一来,每一次的新的item项会被重新绘制界面,并将重绘的界面放入集合中做保存,可以有效的防止item项状态因服用被改变的问题出现。


但是这样的话,复用将大打折扣。内存消耗也会有所提升。


看夏大的博客回复中,有大神提到可以将状态也同时放入tag中做保存,同样在实现复用的时候复用状态。


如有不明确的地方,可以回复,荣幸与各位开发者公共探讨。


原创文章,转载请注明出处:
http://blog.csdn.net/qq_33078541?viewmode=contents

你可能感兴趣的:(android,开发路途)