您的位置:首页 > 移动开发 > Android开发

Android——ListView、Adapter及其优化

2016-07-06 10:24 645 查看


一、Adapter

首先我们来看看他的继承结构图:



上面就是Adapter以及继承结构图了,接着我们介绍一下实际开发中还用到的几个Adapter吧!

BaseAdapter:抽象类,实际开发中我们会继承这个类并且重写相关方法,用得最多的一个Adapter!
ArrayAdapter:支持泛型操作,最简单的一个Adapter,只能展现一行文字~
SimpleAdapter:同样具有良好扩展性的一个Adapter,可以自定义多种效果!
SimpleCursorAdapter:用于显示简单文本类型的listView,一般在数据库那里会用到,不过有点过时, 不推荐使用!

其实一个BaseAdapter就够玩的了,至于其他的,实际开发中用得不多


二、ListView


自定义BaseAdapter,然后绑定ListView的最简单例子

先看看我们要实现的效果图:



关键代码:

Animal.java:

public class Animal {
private String aName;
private String aSpeak;
private int aIcon;

public Animal() {
}

public Animal(String aName, String aSpeak, int aIcon) {
this.aName = aName;
this.aSpeak = aSpeak;
this.aIcon = aIcon;
}

public String getaName() {
return aName;
}

public String getaSpeak() {
return aSpeak;
}

public int getaIcon() {
return aIcon;
}

public void setaName(String aName) {
this.aName = aName;
}

public void setaSpeak(String aSpeak) {
this.aSpeak = aSpeak;
}

public void setaIcon(int aIcon) {
this.aIcon = aIcon;
}
}


AnimalAdapter.java:自定义的BaseAdapter:

public class AnimalAdapter extends BaseAdapter {

private LinkedList<Animal> mData;
private Context mContext;

public AnimalAdapter(LinkedList<Animal> mData, Context mContext) {
this.mData = mData;
this.mContext = mContext;
}

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);
img_icon.setBackgroundResource(mData.get(position).getaIcon());
txt_aName.setText(mData.get(position).getaName());
txt_aSpeak.setText(mData.get(position).getaSpeak());
return convertView;
}
}


最后是MainActivity.java:

public class MainActivity extends AppCompatActivity {

private List<Animal> mData = null;
private Context mContext;
private AnimalAdapter mAdapter = null;
private ListView list_animal;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = MainActivity.this;
list_animal = (ListView) findViewById(R.id.list_animal);
mData = new LinkedList<Animal>();
mData.add(new Animal("狗说", "你是狗么?", R.mipmap.ic_icon_dog));
mData.add(new Animal("牛说", "你是牛么?", R.mipmap.ic_icon_cow));
mData.add(new Animal("鸭说", "你是鸭么?", R.mipmap.ic_icon_duck));
mData.add(new Animal("鱼说", "你是鱼么?", R.mipmap.ic_icon_fish));
mData.add(new Animal("马说", "你是马么?", R.mipmap.ic_icon_horse));
mAdapter = new AnimalAdapter((LinkedList<Animal>) mData, mContext);
list_animal.setAdapter(mAdapter);
}

}


自定义BaseAdapter以及完成数据绑定就是这么简单~


三、优化

我们从代码 中可以看出比较重要的两个方法:getCount()和getView(),界面上有多少列就会调用多少次getView, 这个时候可能看出一些端倪,每次都是新inflate一个View,都要进行这个XML的解析,这样会 很浪费资源,当然,几十列或者几百列的列表并不能体现什么问题,但假如更多或者布局更加复杂? 所以学习ListView的优化很重要,而本节针对的是BaseAdapter的优化,优化的两点有,复用convertView 以及使用ViewHolder重用组件,不用每次都findViewById。


1.复用ConvertView:

上面也说了,界面上有多少个Item,那么getView方法就会被调用多少次! 我们来看看上一节我们写的getView()部分的代码:

<span style="font-size:14px;">@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);

img_icon.setBackgroundResource(mData.get(position).getaIcon());
txt_aName.setText(mData.get(position).getaName());
txt_aSpeak.setText(mData.get(position).getaSpeak());
return convertView;
}</span>


是吧,inflate()每次都要加载一次xml,其实这个convertView是系统提供给我们的可供服用的View 的缓存对象,那就坐下判断咯,修改下,优化后的代码:

<span style="font-size:14px;">@Override
public View getView(int position, View convertView, ViewGroup parent) {

if(convertView == null){
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
}

ImageView img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
TextView txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
TextView txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);

img_icon.setBackgroundResource(mData.get(position).getaIcon());
txt_aName.setText(mData.get(position).getaName());
txt_aSpeak.setText(mData.get(position).getaSpeak());
return convertView;
}</span>



2.ViewHolder重用组件

嘿嘿,getView()会被调用多次,那么findViewById不一样得调用多次,而我们的ListView的Item 一般都是一样的布局,我们可以对这里在优化下,我们可以自己定义一个ViewHolder类来对这一部分 进行性能优化!修改后的代码如下:

<span style="font-size:14px;">@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_animal,parent,false);
holder = new ViewHolder();
holder.img_icon = (ImageView) convertView.findViewById(R.id.img_icon);
holder.txt_aName = (TextView) convertView.findViewById(R.id.txt_aName);
holder.txt_aSpeak = (TextView) convertView.findViewById(R.id.txt_aSpeak);
convertView.setTag(holder);   //将Holder存储到convertView中
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.img_icon.setBackgroundResource(mData.get(position).getaIcon());
holder.txt_aName.setText(mData.get(position).getaName());
holder.txt_aSpeak.setText(mData.get(position).getaSpeak());
return convertView;
}

static class ViewHolder{
ImageView img_icon;
TextView txt_aName;
TextView txt_aSpeak;
}</span>


3.分批加载,滑动的时候不加载图片

上面的两个例子中ListView都是显示的本地的List集合中的内容,List的长度也只有100个,我们可以毫不费力一次性加载完这100个数据;但是实际应用中,我们往往会需要使用Listview来显示网络上的内容,比如说我们拿使用ListView显示新闻为例:
其一:假如网络情况很好,我们使用的手机也许能够一下子加载完所有新闻数据,然后显示在ListView中,用户可能感觉还好,假如说在网络不太顺畅的情况下,用户加载完所有网络的数据,可能这个list是1000条新闻,那么用户可能需要面对一个空白的Activity好几分钟,这个显然是不合适的
其二:我们知道Android虚拟机给每个应用分配的运行时内存是一定的,一般性能不太好的机器只有16M,好一点的可能也就是64M的样子,假如说我们现在要浏览的新闻总数为一万条,即便是网络很好的情况下,我们可以很快的加载完毕,但是多数情况下也会出现内存溢出从而导致应用崩溃的情况。
那么为了解决上面的两个问题,我们需要进行分批加载,比如说1000条新闻的List集合,我们一次加载20条,等到用户翻页到底部的时候,我们再添加下面的20条到List中,再使用Adapter刷新ListView,这样用户一次只需要等待20条数据的传输时间,不需要一次等待好几分钟把数据都加载完再在ListView上显示。其次这样也可以缓解很多条新闻一次加载进行产生OOM应用崩溃的情况。
实际上,分批加载也不能完全解决问题,因为虽然我们在分批中一次只增加20条数据到List集合中,然后再刷新到ListView中去,假如有10万条数据,如果我们顺利读到最后这个List集合中还是会累积海量条数的数据,还是可能会造成OOM的情况,这时候我们就需要用到分页,比如说我们将这10万条数据分为1000页,每一页100条数据,每一页加载时都覆盖掉上一页中List集合中的内容,然后每一页内再使用分批加载,这样用户的体验就会相对好一些。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  listview 优化