讲解这个问题的时候,我们来看下,我的另一篇的博客代码android之自定义适配器实现Listview中getView的代码
@Override
public View getView(int position, View@Override
public View getView(int position, ViewconvertView, ViewGroup parent) {//得到当前条目的view
/*
* 判断是否是否是第一次加载,因为getview会在第一次加载时
* 把其对象全部new出来
*/
if(convertView==null){//判断是否存在view
convertView=inflater.inflate(resource,null);//根据Id,找到其所在的xml,将其转换成view对象
}
//由view对象找到指定的控件
TextView name = (TextView)convertView.findViewById(R.id.name);
TextView telephone = (TextView)convertView.findViewById(R.id.telephone);
//由position找到要绑定的数据,position为当前条目的id
HashMap<String, Object> hashMap =data.get(position);
//将要绑定的数据绑定到指定的控件上
name.setText(hashMap.get("name").toString());
telephone.setText(hashMap.get("tel").toString());
returnconvertView;
}
, ViewGroup parent) {//得到当前条目的view
/*
* 判断是否是否是第一次加载,因为getview会在第一次加载时
* 把其对象全部new出来
*/
if(convertView==null){//判断是否存在view
convertView=inflater.inflate(resource,null);//根据Id,找到其所在的xml,将其转换成view对象
}
//由view对象找到指定的控件
TextView name = (TextView)convertView.findViewById(R.id.name);
TextView telephone = (TextView)convertView.findViewById(R.id.telephone);
//由position找到要绑定的数据,position为当前条目的id
HashMap<String, Object> hashMap =data.get(position);
//将要绑定的数据绑定到指定的控件上
name.setText(hashMap.get("name").toString());
telephone.setText(hashMap.get("tel").toString());
returnconvertView;
}
上面的代码当加载少量数据的时候,是没有问题的,但是当我们加载大量的数据后,就会出现卡顿的现象,其实这个就是内存泄露造成的。它是怎么产生的呢?这是我们每次显示一个listview条目时,每次都会调用一次getView方法创建一个条目view,而上述代码,我们每次显示都是创建了view,当大量view被创建时,这时就可以出现内存泄露的问题了。况且上述代码中每次创建view的同时,我们每次都要去fidnViewById去查找出这个控件来,这也会损耗一些时间性能。知道了原因,我们就可以针对这些原因来给出解决的办法。
1. 每次都创建view,我们可以使用缓存convertView,当我们第一次显示的题目的时候,创建缓存convertView,下次我们当要再次创建的时候,就判断缓存convertView是否为空,不为空就说明缓存中存在view,这时我们使用缓存中的view就行了,这样我们就大大节省了内存。
2. 使用ViewHolder来避免每次都调用findViewById,ViewHolder其实就是我们自己创建的一种静态类(使用静态类缓存了显示数据的视图(View),加快了View的响应速度,当我们判断ConvertView==null的时候,如果为空,就会根据设计好的List加载Item布局(XML)),它用来保存保存findViewById出来的控件。
至于为什么使用convertView+ViewHolder来解决这个问题,我们来引用下官方的解释:
To workefficiently the adapter implemented here uses two techniques:
-It reuses the convertView passed to getView() to avoid inflating View when itis not necessary
(译:重用缓存convertView传递给getView()方法来避免填充不必要的视图)
-It uses the ViewHolder pattern to avoid calling findViewById() when it is notnecessary
(译:使用ViewHolder模式来避免没有必要的调用findViewById():因为太多的findViewById也会影响性能)
ViewHolder类的作用
-The ViewHolder pattern consists in storing a data structure in the tag of theview
returned by getView().This data structures contains references to the views wewant to bind data to,
thus avoiding calling to findViewById() every time getView() is invoked
(译:ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们
要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById())
(注:这个是网上搜索的,不知道是否为官方解释,我查询SDK解释文档没有找到,可能我眼拙吧)
上面就针对我上面给出的解决方法给出我的代码,其实就是在原来的代码上,对getView中的代码进行改进下就OK了。
main.xml为listview的布局文件
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/listview"
/>
</LinearLayout>
list_item.xml此为listview每条目录的布局文件
<?xmlversion="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/name"
android:paddingRight="100dip"
android:textSize="30dip"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30dip"
android:layout_toRightOf="@id/name"
android:layout_alignTop="@id/name"
android:paddingLeft="100dip"
android:id="@+id/telephone"
/>
</RelativeLayout>
自定义适配器:
package com.kun.listview02;
import java.util.HashMap;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
publicclass InfoAdapterextends BaseAdapter {
private List<HashMap<String, Object>>data;//要绑定的数据
privateintresource;//要绑定的那个控件的xml的ID
private LayoutInflaterinflater;
public InfoAdapter(Context context,List<HashMap<String,Object>> data,int resource) {
this.data = data;
this.resource = resource;
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
publicint getCount() {//得到listview所要显示的总数目
returndata.size();
}
@Override
public Object getItem(int position) {//得到第position条条目对象
returndata.get(position);
}
@Override
publiclong getItemId(int position) {//返回其当前条目的当前ID
return position;
}
@Override
public View getView(int position, View convertView, ViewGroupparent) {//得到当前条目的view
ViewHolder holder;
/*
* 判断是否是否是第一次加载,因为getview会在第一次加载时
* 把其对象全部new出来
*/
if(convertView ==null){//判断是否存在view
holder = new ViewHolder();
convertView = inflater.inflate(resource,null);//根据Id,找到其所在的xml,将其转换成view对象
//有view对象找到指定的控件
holder.name = (TextView)convertView.findViewById(R.id.name);
holder.telephone = (TextView)convertView.findViewById(R.id.telephone);
convertView.setTag(holder);//将view设置到缓存中
}else{
System.out.println("11111111111111");
holder = (ViewHolder)convertView.getTag();
/*//有position找到要绑定的数据,position为当前条目的id
HashMap<String, Object> hashMap =data.get(position);
System.out.println("name-->"+hashMap.get("name").toString()+"tel"+hashMap.get("tel").toString());
//将要绑定的数据绑定到指定的控件上
holder.name.setText(hashMap.get("name").toString());
holder.telephone.setText(hashMap.get("tel").toString());*/
}
//有position找到要绑定的数据,position为当前条目的id
HashMap<String, Object> hashMap =data.get(position);
System.out.println("name-->"+hashMap.get("name").toString()+"tel"+hashMap.get("tel").toString());
//将要绑定的数据绑定到指定的控件上
holder.name.setText(hashMap.get("name").toString());
holder.telephone.setText(hashMap.get("tel").toString());
return convertView;
}
/**
* ViewHolder用来缓存findViewById的控件
* @author Administrator
*
*/
staticclass ViewHolder{
public TextViewname;
public TextViewtelephone;
}
}
主Activity
package com.kun.listview02;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
importandroid.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
public class ListView02Activity extendsActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView listView = (ListView)findViewById(R.id.listview);
//生成动态数组,加入数据
List<HashMap<String, Object>> itemview = newArrayList<HashMap<String,Object>>();
for (int i = 0; i < 10; i++) {
HashMap<String, Object>hashMap = new HashMap<String, Object>();
hashMap.put("name","csk");
hashMap.put("tel",110+i+"");
itemview.add(hashMap);
}
InfoAdapter cAdapter = new InfoAdapter(this,itemview, R.layout.list_item);
listView.setAdapter(cAdapter);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public voidonItemClick(AdapterView<?> parent, View view,
int position,long id) {
// TODO Auto-generatedmethod stub
Toast.makeText(getApplicationContext(),"第"+(position+1)+"条数据",Toast.LENGTH_LONG).show();
}
});//添加响应事件
}
}
效果跟第二篇博客一样,就不贴出效果图了。
Ps:不知道为什么注释的代码不能写在那,写在那不能显示数据,只能写在外面。下次看下能不能写下http协议编程。