【Android笔记】BaseAdapter适配器的介绍、使用及优化(详细)

 

什么是数据适配器

 

 

数据源(Data source)的格式是多种多样的,但是ListView的可以展示的格式却是有一定的要求的

作为适配器(Adapter)的作用就是将数据源中多种多样的数据格式转化为ListView可以展示的格式,建立数据源与ListView的一个适配关系,比如数据源的某某格式应该对应ListView支持的某某格式。并从中将数据的来源和数据的显示进行了解耦,降低了程序的耦合性,让程序更容易扩展。

 

ListView的显示与缓存机制

 

 

 

如果存在一百条数据,ListView不会一次性全部加载,它只会加载屏幕能够展示的数据,如图中的7条数据,当手指向上滑动的时候,Item1就会移除屏幕,它就会被回收到一个Recycler的View缓冲池中,而Item8就从会缓存池中取出来一个布局文件,并通过getView()重新设置好Item8要显示的数据,再插入画面中空位中

 

 

 

BaseAdapter通用适配器

 

getCount():就是这个ListView总共要显示多少条数据

 

使用步骤:

第一步,我们要建立好android的界面布局,首先是主要的ListView的布局界面

 

在activity_main.xml写一个ListView,代码如下:

activity_main.xml

         

第二步,再新建一个item布局,作为ListView的子布局

 

布局文件如下:

item.xml

 

                      

 

这个布局是用来给ListView每一项Item显示内容的布局,效果如下

 

 

 

BaseAdapter之创建数据源

之前我们已经将ListView的item项布局已经建好了,但是我们还需要建立一个数据源,通过数据源来给item赋值。

第三步,创建数据源,这里新建一个Java类,叫做itemBean,作用是来封装item布局的三个数据,图片,title,content

ItemBean.java

 

package bnuz.lwj.listviewteacheing;  public class ItemBean { 	public int ItemImageResid; 	public String ItemTitle; 	public String ItemContent; 	 	//构造方法 	public ItemBean(int itemImageResid, String itemTitle, String itemContent) { 		super(); 		ItemImageResid = itemImageResid; 		ItemTitle = itemTitle; 		ItemContent = itemContent; 	} 	 	  }

将数据封装起来,每一个ItemBean对象就对应一个Item布局的内容

 

第四步,我们要给数据源赋值,然后我们切换在MainActivity的onCreate()函数中写一个for循环,用于 赋值、调试

 

List itemBeanList=new ArrayList(); for(int i=0;i<20;i++){ 	itemBeanList.add(new ItemBean(R.drawable.ic_launcher,"我的标题"+i,"我是内容"+i)); }

 

 

BaseAdapter之数据适配器初解

经过上面的操作,我们已经将数据源建立起来了,然后我们需要重写一个数据适配器

第五步、重写数据适配器,新建一个JAVA类,MyAdapter.java,继承于BaseAdapter,重写四个函数,我们可以看到这四个函数就是我们之间在上面介绍BaseAdapter通用适配器的时候图片所展示出来的四个函数(如果没看懂,就上去看上边的图片,BaseAdapter的基本结构)

 

为了从数据源中要获取传进来的数据,通常在适配器中对数据进行初始化

这里贴出前三个函数的重写方法,每一个函数的作用都写在了注释了,可以参考上边BaseAdapter的基本结构的图一一对应,好好琢磨,会想通的

//私人成员属性,用于保存传进来的数据 	private List  mList; 	//构造方法,用于初始化传进来的参数 	//这里需要传进来一个context对象来初始化 mInflater 	public MyAdapter(Context context,List list) { 		mList=list; 		mInflater=LayoutInflater.from(context); 	} 	@Override 	public int getCount() { 		// 返回ListView需要显示的数据 		return mList.size(); 	}  	@Override 	public Object getItem(int position) { 		//所有的数据(集中)项数据都存放在mList中 		//取出对应索引的数据项的数据并返回 		return mList.get(position); 	}  	@Override 	public long getItemId(int position) { 		// 返回某个数据项对应的索引 		return position; 	}

 

getView()的三种使用方法

第四个函数getView()即是最重要也是最复杂的一个函数,所以一定要弄懂这个函数。

同时这个函数,这里总结三个方法。

 

第一个方法仔细的总结一下,第二第三个方法就贴代码,不一一总结了。

 

第一个方法:

 

首先我们要在MyAdapter函数里,创建一个LayoutInflater对象,它的作用是将XML文件转化为一个View布局,然后 通过LayoutInflater.from(context)来初始化

 

 

 

然后在getView函数里,新定义一个View的对象来获取mInflater装载的item布局的对象。

 

然后我们需要将getItem()函数从数据源中取得并返回来的数据赋值我们的item.xml里的三个控件或则新定义一个ItemBean,再通过List集合的get索引(position)方法来获取在mList的数据项,在这里,我们使用第二种方法。

第一种方法的完整代码

 

@Override 	public View getView(int position, View convertView, ViewGroup parent) { 		//这是最重要也是最复杂的方法 		//作用是返回每一项的内容 	//方法一、效率低下 		//将item.xml转化为View布局 		View view =mInflater.inflate(R.layout.item,null); 		ImageView imageView=(ImageView) view.findViewById(R.id.iv_image); 		TextView title=(TextView) view.findViewById(R.id.tv_title); 		TextView content=(TextView) view.findViewById(R.id.tv_content); 		//赋值 		ItemBean bean =mList.get(position);				 		imageView.setImageResource(bean.ItemImageResid); 		title.setText(bean.ItemTitle); 		content.setText(bean.ItemContent); 		 		return view; 		 	 	}

 

 

 

但是方法一没有使用到ListView的缓存机制,这是对资源的极大浪费,效率低下,所以不推荐使用

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

第二个方法:

 

第二种方法的完整代码

 

@Override 	public View getView(int position, View convertView, ViewGroup parent) { 		//这是最重要也是最复杂的方法 		//作用是返回每一项的内容 	 		//方法二、正常,利用了ListView的缓存机制,算入门,findViewById会浪费大量时间 		//如果为空:View未被实例化,缓存池中也无缓存,所以我们要主动为它赋一个View 		if(convertView==null){ 			convertView=mInflater.inflate(R.layout.item,null); 		} 		ImageView imageView=(ImageView)convertView.findViewById(R.id.iv_image); 		TextView title=(TextView) convertView.findViewById(R.id.tv_title); 		TextView content=(TextView) convertView.findViewById(R.id.tv_content); 		//赋值 		ItemBean bean =mList.get(position);				 		imageView.setImageResource(bean.ItemImageResid); 		title.setText(bean.ItemTitle); 		content.setText(bean.ItemContent); 		return convertView; 	 	 	}

 

 
 
 
 
 
 
 

第二种方法虽然使用了ListView的缓存机制,算入门方式,但是其中每次都会重复调用多次findViewById,也会浪费资源,所以谷歌的大佬提供了第三种方式

 

第三个方法:

首先在MyAdapter类里定义一个内部类ViewHolder类,作用就是为了避免重复的findViewById操作

 

//创建一个内部类,作业就是为了避免重复的findViewById操作 	class ViewHolder{ 		//对应item.xml的三个控件 		public ImageView imageView; 		public TextView title; 		public TextView content; 	} 

第三种方法的完整代码

 

@Override 	public View getView(int position, View convertView, ViewGroup parent) { 		//这是最重要也是最复杂的方法 		//作用是返回每一项的内容 	 		//方法三、建议使用 		ViewHolder viewHolder; 		if(convertView==null){ 			viewHolder=new ViewHolder(); 			convertView=mInflater.inflate(R.layout.item,null); 			//将控件保存到viewHolder中 			viewHolder.imageView=(ImageView)convertView.findViewById(R.id.iv_image); 			viewHolder.title=(TextView) convertView.findViewById(R.id.tv_title); 			viewHolder.content=(TextView) convertView.findViewById(R.id.tv_content); 			//通过setTag将ViewHoler与convertView绑定 			convertView.setTag(viewHolder);			 		}else{ 			viewHolder=(ViewHolder) convertView.getTag();					 		} 		ItemBean bean =mList.get(position);				 		viewHolder.imageView.setImageResource(bean.ItemImageResid); 		viewHolder.title.setText(bean.ItemTitle); 		viewHolder.content.setText(bean.ItemContent); 		 		return convertView; 	 	 	}

 

 
 
 
 
 
 
 
 

作用就是通过ViewHodler,当converView为空的时候,我们为其赋值,并且通过converView.setTag(viewHolder)的方法将holder与converView进行绑定,之后的遍历中,每次只要通过converView的getTag()方法就能获取到converView的三个控件,避免了每次都通过findById这个方法去实例化三个控件。

 

 

MyAdapter.java

 

package bnuz.lwj.listviewteacheing;  import java.util.List; import bnuz.lwj.listviewteacheing.ItemBean; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView;  public class MyAdapter extends BaseAdapter{ 	//创建一个LayoutInflater 	private LayoutInflater mInflater; 	//私人成员属性,用于保存传进来的数据 	private List  mList; 	//构造方法,用于初始化传进来的参数 	//这里需要传进来一个context对象来初始化 mInflater 	public MyAdapter(Context context,List list) { 		mList=list; 		mInflater=LayoutInflater.from(context); 	} 	@Override 	public int getCount() { 		// 返回ListView需要显示的数据 		return mList.size(); 	}  	@Override 	public Object getItem(int position) { 		//所有的数据(集中)项数据都存放在mList中 		//取出对应索引的数据项的数据并返回 		return mList.get(position); 	}  	@Override 	public long getItemId(int position) { 		// 返回某个数据项对应的索引 		return position; 	}  	@Override 	public View getView(int position, View convertView, ViewGroup parent) { 		//这是最重要也是最复杂的方法 		//作用是返回每一项的内容 	//方法一、效率低下 		//将item.xml转化为View布局 		/*View view =mInflater.inflate(R.layout.item,null); 		ImageView imageView=(ImageView) view.findViewById(R.id.iv_image); 		TextView title=(TextView) view.findViewById(R.id.tv_title); 		TextView content=(TextView) view.findViewById(R.id.tv_content); 		//赋值 		ItemBean bean =mList.get(position);				 		imageView.setImageResource(bean.ItemImageResid); 		title.setText(bean.ItemTitle); 		content.setText(bean.ItemContent); 		 		return view;*/ 		 	//方法二、正常,利用了ListView的缓存机制,算入门,findViewById会浪费大量时间 		//如果为空:View未被实例化,缓存池中也无缓存,所以我们要主动为它赋一个View 		/*if(convertView==null){ 			convertView=mInflater.inflate(R.layout.item,null); 		} 		ImageView imageView=(ImageView)convertView.findViewById(R.id.iv_image); 		TextView title=(TextView) convertView.findViewById(R.id.tv_title); 		TextView content=(TextView) convertView.findViewById(R.id.tv_content); 		//赋值 		ItemBean bean =mList.get(position);				 		imageView.setImageResource(bean.ItemImageResid); 		title.setText(bean.ItemTitle); 		content.setText(bean.ItemContent); 		return convertView;*/ 	//方法三、建议使用 		ViewHolder viewHolder; 		if(convertView==null){ 			viewHolder=new ViewHolder(); 			convertView=mInflater.inflate(R.layout.item,null); 			//将控件保存到viewHolder中 			viewHolder.imageView=(ImageView)convertView.findViewById(R.id.iv_image); 			viewHolder.title=(TextView) convertView.findViewById(R.id.tv_title); 			viewHolder.content=(TextView) convertView.findViewById(R.id.tv_content); 			//通过setTag将ViewHoler与convertView绑定 			convertView.setTag(viewHolder);			 		}else{ 			viewHolder=(ViewHolder) convertView.getTag();					 		} 		ItemBean bean =mList.get(position);				 		viewHolder.imageView.setImageResource(bean.ItemImageResid); 		viewHolder.title.setText(bean.ItemTitle); 		viewHolder.content.setText(bean.ItemContent); 		 		return convertView; 	} 	 	//创建一个内部类,作业就是为了避免重复的findViewById操作 	class ViewHolder{ 		//对应item.xml的三个控件 		public ImageView imageView; 		public TextView title; 		public TextView content; 	}  }

这里将方法一、二都注释掉,第三种方法不仅仅利用了ListView的缓存机制,更通过ViewHolder类来实现显示数据的视图的缓存,避免了多次通过调用findViewById寻找控件

所以强烈建议使用第三种方法

 

ListView调用BaseAdapter适配器

 

第六步,在MainActivity中,我们通过setAdapter来讲view对象的数据传递到ListView对象中

 

MainActivity.java

package bnuz.lwj.listviewteacheing;  import java.util.ArrayList; import java.util.List;  import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget.ListView;  public class MainActivity extends Activity { 	ListView listView; 	@Override 	protected void onCreate(Bundle savedInstanceState) { 		super.onCreate(savedInstanceState); 		setContentView(R.layout.activity_main); 		listView=(ListView) findViewById(R.id.lv_main); 		List itemBeanList=new ArrayList(); 		for(int i=0;i<20;i++){ 			itemBeanList.add(new ItemBean(R.drawable.ic_launcher,"我的标题"+i,"我是内容"+i)); 		} 		//通过这个函数,我们可以将一个MyAdpter对象传递给我们的ListView 		//从而达到将数据源中多种多样的数据格式转化为ListView可以展示的格式 		//因为在MyAdpter中,已经将数据进行处理,赋值给View并返回过来 		listView.setAdapter(new MyAdapter(this,itemBeanList)); 	} } 

最后结果展示

 

总结就到此结束~~~

 

 

第一次码博客,就请各位大佬高抬贵手,本总结是看了慕课网的大佬后写的,截图也是出自视频

完整源码下载点击这里

 

欢迎关注我的博客,一起学习讨论

要转载,请附上原文链接,作者:SnailMann

 

可以关注我的私人github: https://github.com/SnailMann,欢迎watch ,star, fork

关注我的私人GitHub

虽然现在暂时没有什么东西,但是总会有的大笑