什么是数据适配器
数据源(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
虽然现在暂时没有什么东西,但是总会有的