优化ListView加载数据逻辑

一、ListView介绍

在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候,会占据大量内存,影响性能,这时候就需要按需填充并重新使用view来减少对象的创建。
它以列表的形式展示数据内容,并且能够根据列表的高度自适应屏幕显示

二、表现形式

在这里插入图片描述

这就是一种最简单的 ListView 的表现形式,黑色框就是 ListView 控件,其中由一个个的 item 组成(红色框内容),然后可以通过向下滑动来查看很多的条目。

三、工作原理

ListView 仅是作为容器(列表),用于装载显示数据(就是上面的一个个的红色框的内容,也称为 item)。item 中的具体数据是由适配器(adapter)来提供的。
当需要显示数据的时候,ListView 会从适配器(Adapter)中取出数据,然后来加载数据。
在这里插入图片描述

四、常用的数据适配器(Adapter)

1.BaseAdapter

BaseAdapter是基本的适配器。其实实际上就是一个抽象类,通常在自定义适配器时会继承BaseAdapter,该类拥有四个抽象方法,根据这几个抽象方法来对ListView控件进行数据适配。

BaseAdapter的四个抽象方法:

方法名称功能 描述
public int getCount()获取Item条目的总数
public Object getItem(int position)根据position(位置)获取某个Item的对象
public long getItemId(int position)根据position(位置)获取某个Item的id
public View getView(int position,View convertView,ViewGroup parent)获取相应position对应的Item视图,position是当前Item的位置,converView用于复用旧视图,parent用于加载XML布局

2.SimpleAdapter

SimpleAdapter继承自BaseAdapter,实现了BaseAdapter的四个抽象方法并对其进行封装。因此在使用SimpleAdapter进行数据适配时,只需要在构造方法中传入相应的参数即可,SimpleAdapter的构造方法的具体信息如下:

public SimpleAdapter(Context context,List<? extends Map<String,?>> data,int resourse,String[] from,int[] to)

在SimpleAdapter()构造方法中的5个参数的含义:

  • context:表示上下文对象
  • data:数据集合,data中的每一项对应ListView控件中的条目的数据
  • resourse:Item布局的资源id
  • from:Map集合中的key值
  • to:Item布局中对应的控件

3.ArrayAdapter

ArrayAdapter也是BaseAdapter的子类,用法与SimpleAdapter类似,开发者只需要在构造方法里面传入相应参数即可。ArrayAdapter通常用于适配TextView控件,例如Android系统中的Setting(设置菜单)。ArrayAdapter有多个构造方法,ArrayAdapter构造方法具体信息如下:

public ArrayAdapter(Context context,int resourse);
public ArrayAdapter(Context context,int resourse,int textViewResourceId);
public ArrayAdapter(Context context,int resourse,T[] objects);
public ArrayAdapter(Context context,int resourse,int textViewResourceId,T[] objects);
public ArrayAdapter(Context context,int resourse,List<T> objects);
public ArrayAdapter(Context context,int resourse,int textViewResourceId,List<T> objects);

在ArrayAdapter()构造方法中的5个参数的含义:

  • context:表示上下文对象
  • resourse:Item布局的资源id
  • textViewResourceId:Item布局中相应TextView的id
  • T[] objects:需要适配的数组类型的数据
  • List objects:需要适配的List类型的数据

五、优化方式

优化listview的加载速度就要让convertView匹配列表类型,并最大程度上的重新使用convertView。

getview的加载方法一般有以下三种种方式:

1.每一次都重新定义一个View载入布局,再加载数据(最慢的加载方式)

public View getView(int position, View convertView, ViewGroup parent) {
 View item = mInflater.inflate(R.layout.list_item_icon_text, null);

 ((TextView) item.findViewById(R.id.text)).setText(DATA[position]);

 ((ImageView) item.findViewById(R.id.icon)).setImageBitmap(

 (position & 1) == 1 ? mIcon1 : mIcon2);

 return item;

}

2.convertView不为空的时候直接重新使用

从而减少了很多不必要的View的创建,然后加载数据

public View getView(int position, View convertView, ViewGroup parent) {
 if (convertView == null) {
 convertView = mInflater.inflate(R.layout.item, parent, false);

 }

 ((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]);

 ((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap(

 (position & 1) == 1 ? mIcon1 : mIcon2);

 return convertView;

 }

3.定义一个ViewHolder

将convetView的tag设置为ViewHolder,不为空时重新使用即可(最快的方式)

static class ViewHolder {
TextView text;

ImageView icon;

}

 

public View getView(int position, View convertView, ViewGroup parent) {
 ViewHolder holder;

 

 if (convertView == null) {
 convertView = mInflater.inflate(R.layout.list_item_icon_text,

 parent, false);

 holder = new ViewHolder();

 holder.text = (TextView) convertView.findViewById(R.id.text);

 holder.icon = (ImageView) convertView.findViewById(R.id.icon);

 convertView.setTag(holder);

} else {
holder = (ViewHolder) convertView.getTag();

}

holder.text.setText(DATA[position]);

holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);

return convertView;

}

六、实例优化

在这里插入图片描述
运行程序后,当ListView控件加载的Item过多过快并快速移动该空间时,界面会出现卡顿的现象,出现这个现象的原因有这么几个:

  1. 当滑动屏幕时,不断创建Item对象。ListView控件在当前屏幕上显示多少个Item,就会在适配器MyBaseAdapter中的getView()方法中创建多少Item对象。当滑动ListView控件时,滑出屏幕的Item对象会被销毁,新增加到屏幕上的Item会创建新的对象,因此快速滑动ListView控件时会不断地对Item对象进行销毁和创建。
  2. 不断进行findViewById()方法初始化控件。每创建一个Item对象都需要加载一次Item布局,加载布局时会不断地执行findViewById()方法初始化控件。这些操作比较耗费设备的内存并且浪费时间,如果每个Item都需要加载网络图片,加载网络图片是个比较耗时的操作,就会造成程序内存溢出的异常。

由于上述两点原因,我们需要对ListView控件进行优化,优化的的目的是使ListView控件在快速滑动时不再重复创建Item对象,减少内存的消耗和屏幕渲染的处理。优化步骤如下:

  1. 创建ViewHolder类。在MainActivity中创建一个ViewHolder类,将需要加载的控件变量放在该类中。代码如下:
class ViewHolder{
      TextView title,price;
      ImageView iv;
}
  1. 在MyBaseAdapter的getView(int position,View convertView,ViewGroup parent)方法中,第2个参数convertView代表的就是之前滑出屏幕的Item对象。如果是第一次加载getView()方法时,会创建Item对象,当滑动ListView控件时,滑出屏幕的Item对象会以缓存的形式存在,而convertView代表的就是缓存的Item对象,我们可以通过复用convertView对象从而减少Item对象的创建,在getView()方法中优化。代码如下:
public View getView(int position,View convertView,ViewGroup parent){
	ViewHolder holder = null;
	if(convertView==null){
 		convertView = View.inflate(MainActivity.this,R.layout.list_item,null);
 		holder = new ViewHolder();
 		holder.title = (TextView) convertView.findViewById(R.id.title);
 		holder.title = (TextView) convertView.findViewById(R.id.price);
 		holder.iv = (ImageView) convertView.findViewById(R.id.iv);
 		convertView.setTag(holder);
 }else{
	holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(titles[position]);
holder.price.setText(prices[position]);
holder.iv.setBackgroundResource(icons[position]);
return convertView;
	}
}

第2行到19行代码主要是用于判断convertView对象是否为null,如果为null,则会创建ViewHolder类的对象holder,并将获取界面控件赋值给ViewHolder类中的属性,最后通过setTag()方法将对象holder添加到convertView对象中,否则,不会重新创建ViewHolder类的对象,会通过getTag()方法获取缓存在convertView对象中的ViewHolder类的对象。


版权声明:本文为weixin_46311652原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。