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

2023年7月21日08:08:12

 

什么是数据适配器

 

 

数据源(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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

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

 

布局文件如下:

item.xml

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/iv_image"
        android:gravity="center"
        android:layout="@+id/iv_image"
        android:text="title" />

     <TextView
         android:id="@+id/tv_content"
         android:layout_width="match_parent"
         android:layout_height="30dp"
         android:layout_alignBottom="@+id/iv_image"
         android:layout_alignLeft="@+id/tv_title"
         android:layout_alignParentRight="true"
         android:gravity="center_vertical"
         android:layout="@+id/iv_image"
         android:text="content" />

</RelativeLayout>

 

这个布局是用来给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<ItemBean> itemBeanList=new ArrayList<ItemBean>();
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 <ItemBean> mList;
	//构造方法,用于初始化传进来的参数
	//这里需要传进来一个context对象来初始化 mInflater
	public MyAdapter(Context context,List<ItemBean> 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 <ItemBean> mList;
	//构造方法,用于初始化传进来的参数
	//这里需要传进来一个context对象来初始化 mInflater
	public MyAdapter(Context context,List<ItemBean> 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<ItemBean> itemBeanList=new ArrayList<ItemBean>();
		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

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

 

 

  • 作者:SnailMann
  • 原文链接:https://blog.csdn.net/SnailMann/article/details/72773336
    更新时间:2023年7月21日08:08:12 ,共 10197 字。