Android Adapter浅谈
2014-03-05 17:22
162 查看
本文部分内容启发于:http://www.cnblogs.com/allin/archive/2010/05/11/1732200.html
首先上类图:
一、在程序开发中,我们常用到ListView,使用ListView需要三个元素:
1、ListView控件:展示每条数据的框架,是一个ViewGroup;
2、Adapter适配器:连接数据与展示View的中介,通知ListView绘制多少View,每条View怎么绘制,View内部事件如何响应;
3、数据:具体被映射的字符串、图片、状态等信息,有特定的格式要求,具体视Adapter实现而定;
二、适配器Adapter的基础功能定义在接口Adapter中,包括10个方法:
1、registerDataSetObserver/unregisterDataSetObserver:注册/撤销Adapter关联数据发生变化的监察者,注册后可以完成重绘界面等操作;
2、getCount:返回当前Adapter需要绘制多少条数据,ListView按返回值绘制N行View;
3、getItem(int position):返回当前Adapter持有的对应position的数据,返回值对应ListView绘制的某一行展现的数据;
4、getItemId(int position):返回当前Adapter对应位置的数据的唯一标识id。通常如果没有就返回position,如果是数据库内读出的数据就返回主键,如果item对应有int的唯一标识符就返回该值,该值如果设置不正确,可能会导致ListView的onItemClick内的操作对象不是你要处理的对象,比如你删某个item,却删了另一个,或者删除不起作用;
5、hasStableIds:返回当前Adapter持有的数据实体们是否有稳定的唯一标识id,比如4中讲到的返回position是不稳定的,可能随着数据变化导致其值变化,此时需要 返回false,如果返回主键或者唯一标识符则该方法可以返回true。其作用是:返回true,ListView会依据4中的返回的id来确定当前显示哪条内容,也就是firstVisibleChild的位置。
6、getView(int position, View convertView, ViewGroup parent):返回当前Adapter对应position应该绘制的View,这个View可以千奇百怪,需要配套getItemViewType使用,一个列表内所有数据展示都一模一样的就不需要了;
7、getItemViewType(int position):返回当前Adapter对应position位置数据的View的类型,比如你第一行绘制了一个TextView,第二行绘制了一个ImageView,在ListView滚动的时候,第二行肯定不能复用来显示第一行应该显示的数据,这时候就让它们返回不同的ViewType,ListView控件就不会不识趣了,这里需要注意AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER
= -2 已经被占用了,ITEM_VIEW_TYPE_IGNORE = -1 也没了,所以我们用正数一般都没事;
8、getViewTypeCount:返回当前Adapter持有多少种ViewType,6中返回了多少种,这里写总数就行;
9、isEmpty:返回当前Adapter是否持有数据,官方文档说法是:它用于判断空的View是否需要展示,如果没有headers和footers,返回getCount==0就好了,否则可以自定义;
这些里面我们一般情况下主要和getCount、getItem(int position)、getItemId(int position)、getView打交道,因为BaseAdapter已经实现了上面的其他方法;
三、Adapter的继承者包括ListAdapter和SpinnerAdapter,也是接口。
1、
其中ListAdapter连接ListView和数据,增加了判断ListView中绘制的View是否是分隔符的接口,定义了两个方法:
1)areAllItemsEnabled:返回true标识所有的item对应的View都是可点击可选择的,也就是没有分隔符;
2)isEnabled(int position):返回true标识当前Adapter对应position位置数据不是一个分隔符,因为分隔符不可点击且不可选择;
2、
SpinnerAdapter连接Spinner和数据,获取Spinner的下拉框内的显示控件,只定义了一个方法:
1)getDropDownView(int position, View convertView, ViewGroup parent):返回当前Adapter对应position应该绘制的下拉框内的View;
四、扩充的接口往下就是我们比较熟悉的类BaseAdapter了,还有个扩展接口WrapperListAdapter。
1、
我们先讲WrapperListAdapter,它定义了一个包裹另一个ListAdapter的类,增加了一个实现方法:
1)getWrappedAdapter:返回被包裹的ListAdapter;
在android系统内,系统只提供了它的一个实现类——HeaderViewListAdapter,也就是包含Header View和Footer View的ListAdapter,其内部维持了列表头尾的增加删除等操作,且封装了一层ListAdapter,从而为ListView提供列表头尾的管理与数据关联工作。该类还实现了Filterable接口,对外提供筛选功能(自动匹配功能,就是电话簿中输入号码查找出合适联系人的功能);
2、
下面我们详细讲一下BaseAdapter,因为它是我们常用到的ArrayAdapter、SimpleAdapter、CursorAdapter和自定义Adapter的父亲。
BaseAdapter非常简单,绝大部分都是接口的默认实现,它持有一个android.database.DataSetObservable的对象,通过该对象注册/注销数据观察者,并在接口之外提供了两个方法:
1)notifyDataSetChanged:外部或内部调用通知数据观察者适配器内持有的数据发生了改变,view监听到该事件后可以刷新界面;
2)notifyDataSetInvalidated:外部或内部调用通知数据观察者适配器内持有的数据已经失效,注意:按照官方解释,该方法一旦调用,这个Adapter就失效了,也就不能接着报告数据变化事件;
这两个方法都调用DataSetObservable对象的方法实现;
BaseAdapter的其他默认实现包括:
1)hasStableIds 返回false,也就是默认itemId不稳定,如果我们有稳定id,可以覆写为true;
2)areAllItemsEnabled 返回true,也就是默认没有分隔符,所有item都可点击可选择;
3)isEnabled(int position) 返回true,同2;
4)getDropDownView 调用了getView;
5)getItemViewType(int position) 返回0,意味着默认情况下所有我们通过getView生成的View都是同类型的;
6)getViewTypeCount 返回1,依据5;
7)isEmpty 返回getCount==0,因为没有持有列表头尾;
五、BaseAdapter的继承者们就是我们做界面开发直接接触的类了,它们包括:ArrayAdapter<T>、SimpleAdapter、CursorAdapter和自定义Adapter。这里略过CursorAdapter,没有用过,以后看机会再补上。
1、ArrayAdapter
ArrayAdapter只能用于显示一行文字,用法一般如下:
1)setDropDownViewResource(int resource):供Spinner使用,设置下拉框显示字符串的View资源样式,也就是一个xml文件,文件里只能定义一个TextView的子类,不能加LinearLayout之类的,要注意。
2)setNotifyOnChange(boolean notifyOnChange):设置为false可以阻止界面知道数据发生了变化,直到外部调用notifyDataSetChanged将mNotifyOnChange开关打开为止;
2、SimpleAdapter
SimpleAdapter相对而言比ArrayAdapter自由很多,我们可以用自己的定义的界面,可以展现很多种数据,包括选中状态、文字、图片等;它的缺点就是数据格式太死板,我们必须使用List<? extends Map<String, ?>>作为数据源,这个转化过程很没有美感,且耗时;
android实现的SimpleAdapter默认只允许待显示对象为Checkable、TextView、ImageView的数据,用法一般如下:
1)布局:
2)代码
3) 效果:
当然,SimpleAdapter也不是只能显示这三种数据类型,它有个内部接口ViewBinder,只要自己实现一个ViewBinder的子类,并通过setViewBinder(ViewBinder viewBinder)赋值给SimpleAdapter,我们也可以定义对其他的控件进行赋值。这是SimpleAdapter的扩展性。
不过,说实话瑜不掩瑕,如果数据格式不是Map的,不建议用SimpleAdapter,费脑筋。
六、万能的自定义Adapter
好了,我们为了讲解ListView的适配器Adapter,从Adapter接口往下讲了四层了,到了这里,就是故事的高潮部分。我们讲自定义Adapter,到目前为止,能装在ListView里面的东西只有Checkable、String和Bitmap,这明显不够用,但是android就给了这么点功能,所以我们就得自己动手DIY了。
自定义Adapter,一般都继承BaseAdapter,因为BaseAdapter替我们实现了Adapter的基本功能,包括数据变化通知、部分接口实现等等,虽然ListView的setAdapter方法只要实现了ListAdapter接口的就可以用,但是有现成的,我们干嘛要再实现一遍那十几个接口呢,对吧!~
我们先说一下自定义Adapter的好处:
1)数据存储方式灵活:随便什么格式的,只要自己知道格式,就能往里面放;
2)界面展现方式灵活:你可以任意布局摆弄数据,可以加自定义的分隔符,可以加按钮等等等等;
好了,好处说完了就开始动手了。真写起来,其实自定义Adapter没有啥特别的,继承BaseAdapter,如前文所叙,里面有四个方法是需要我们搞定的,分别是:
1)getCount(),返回传入我们定义的Adapter内的数据列表长度,用于通知ListView绘制多少行。
2)getItem(int position),返回传入的数据列表的第position个数据。
3)getItemId(int position),返回传入的数据列表的第position个数据对应的唯一标识符,强烈建议不要返回position。
4)getView(int position, View convertView, ViewGroup parent),返回传入的数据列表的第position个数据对应在ListView的行内应该怎么绘制,我们甚至可以判断一下position,然后在第三行绘制一个ImageView,展现一个深情款款的大猪头。当然,如果其他各行都不是ImageView的话,我们一定要覆写getItemViewType(int position)和getViewTypeCount(),因为BaseAdapter默认每行类型都是0,总的TypeCount为1。
这里有个两个需要注意的地方:
1)convertView会为当前position带来一个可用的view,是给我们复用的,作用是当滚动导致ListView内某行View不可见时,直接复用该View来展示第position个数据,这样能大大减少内存使用量,不管要显示的数据是不是几千条,一般显示一行字的ListView最多创建10几个View就可以满足显示要求了,所以一定得用上。
2)View有个setTag方法,而我们使用findViewById是要查找View树的,相对而言,创建一个内部类ViewHolder并设为View的Tag,会在复用convertView时直接提供对象引用(如TextView、ImageView等等),所以ViewHolder也应该用上。
下面上代码:
1)布局my_adapter_demo.xml
2)数据体Data.java
3)自定义Adapter MyAdapter.java
4)主界面 MainActivity.java
通过这个例子我们可以验证,不同ViewType的View是不会作为convertView使用的,另外往下滚动时,上方不可见的View会被复用来显示下方将要显示出来的数据。
好了,整个Android的ListView Adapter就介绍到这里,有后续需要注意的地方,我会接着这篇文章往下写的。
首先上类图:
一、在程序开发中,我们常用到ListView,使用ListView需要三个元素:
1、ListView控件:展示每条数据的框架,是一个ViewGroup;
2、Adapter适配器:连接数据与展示View的中介,通知ListView绘制多少View,每条View怎么绘制,View内部事件如何响应;
3、数据:具体被映射的字符串、图片、状态等信息,有特定的格式要求,具体视Adapter实现而定;
二、适配器Adapter的基础功能定义在接口Adapter中,包括10个方法:
1、registerDataSetObserver/unregisterDataSetObserver:注册/撤销Adapter关联数据发生变化的监察者,注册后可以完成重绘界面等操作;
2、getCount:返回当前Adapter需要绘制多少条数据,ListView按返回值绘制N行View;
3、getItem(int position):返回当前Adapter持有的对应position的数据,返回值对应ListView绘制的某一行展现的数据;
4、getItemId(int position):返回当前Adapter对应位置的数据的唯一标识id。通常如果没有就返回position,如果是数据库内读出的数据就返回主键,如果item对应有int的唯一标识符就返回该值,该值如果设置不正确,可能会导致ListView的onItemClick内的操作对象不是你要处理的对象,比如你删某个item,却删了另一个,或者删除不起作用;
5、hasStableIds:返回当前Adapter持有的数据实体们是否有稳定的唯一标识id,比如4中讲到的返回position是不稳定的,可能随着数据变化导致其值变化,此时需要 返回false,如果返回主键或者唯一标识符则该方法可以返回true。其作用是:返回true,ListView会依据4中的返回的id来确定当前显示哪条内容,也就是firstVisibleChild的位置。
6、getView(int position, View convertView, ViewGroup parent):返回当前Adapter对应position应该绘制的View,这个View可以千奇百怪,需要配套getItemViewType使用,一个列表内所有数据展示都一模一样的就不需要了;
7、getItemViewType(int position):返回当前Adapter对应position位置数据的View的类型,比如你第一行绘制了一个TextView,第二行绘制了一个ImageView,在ListView滚动的时候,第二行肯定不能复用来显示第一行应该显示的数据,这时候就让它们返回不同的ViewType,ListView控件就不会不识趣了,这里需要注意AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER
= -2 已经被占用了,ITEM_VIEW_TYPE_IGNORE = -1 也没了,所以我们用正数一般都没事;
8、getViewTypeCount:返回当前Adapter持有多少种ViewType,6中返回了多少种,这里写总数就行;
9、isEmpty:返回当前Adapter是否持有数据,官方文档说法是:它用于判断空的View是否需要展示,如果没有headers和footers,返回getCount==0就好了,否则可以自定义;
这些里面我们一般情况下主要和getCount、getItem(int position)、getItemId(int position)、getView打交道,因为BaseAdapter已经实现了上面的其他方法;
三、Adapter的继承者包括ListAdapter和SpinnerAdapter,也是接口。
1、
其中ListAdapter连接ListView和数据,增加了判断ListView中绘制的View是否是分隔符的接口,定义了两个方法:
1)areAllItemsEnabled:返回true标识所有的item对应的View都是可点击可选择的,也就是没有分隔符;
2)isEnabled(int position):返回true标识当前Adapter对应position位置数据不是一个分隔符,因为分隔符不可点击且不可选择;
2、
SpinnerAdapter连接Spinner和数据,获取Spinner的下拉框内的显示控件,只定义了一个方法:
1)getDropDownView(int position, View convertView, ViewGroup parent):返回当前Adapter对应position应该绘制的下拉框内的View;
四、扩充的接口往下就是我们比较熟悉的类BaseAdapter了,还有个扩展接口WrapperListAdapter。
1、
我们先讲WrapperListAdapter,它定义了一个包裹另一个ListAdapter的类,增加了一个实现方法:
1)getWrappedAdapter:返回被包裹的ListAdapter;
在android系统内,系统只提供了它的一个实现类——HeaderViewListAdapter,也就是包含Header View和Footer View的ListAdapter,其内部维持了列表头尾的增加删除等操作,且封装了一层ListAdapter,从而为ListView提供列表头尾的管理与数据关联工作。该类还实现了Filterable接口,对外提供筛选功能(自动匹配功能,就是电话簿中输入号码查找出合适联系人的功能);
2、
下面我们详细讲一下BaseAdapter,因为它是我们常用到的ArrayAdapter、SimpleAdapter、CursorAdapter和自定义Adapter的父亲。
BaseAdapter非常简单,绝大部分都是接口的默认实现,它持有一个android.database.DataSetObservable的对象,通过该对象注册/注销数据观察者,并在接口之外提供了两个方法:
1)notifyDataSetChanged:外部或内部调用通知数据观察者适配器内持有的数据发生了改变,view监听到该事件后可以刷新界面;
2)notifyDataSetInvalidated:外部或内部调用通知数据观察者适配器内持有的数据已经失效,注意:按照官方解释,该方法一旦调用,这个Adapter就失效了,也就不能接着报告数据变化事件;
这两个方法都调用DataSetObservable对象的方法实现;
BaseAdapter的其他默认实现包括:
1)hasStableIds 返回false,也就是默认itemId不稳定,如果我们有稳定id,可以覆写为true;
2)areAllItemsEnabled 返回true,也就是默认没有分隔符,所有item都可点击可选择;
3)isEnabled(int position) 返回true,同2;
4)getDropDownView 调用了getView;
5)getItemViewType(int position) 返回0,意味着默认情况下所有我们通过getView生成的View都是同类型的;
6)getViewTypeCount 返回1,依据5;
7)isEmpty 返回getCount==0,因为没有持有列表头尾;
五、BaseAdapter的继承者们就是我们做界面开发直接接触的类了,它们包括:ArrayAdapter<T>、SimpleAdapter、CursorAdapter和自定义Adapter。这里略过CursorAdapter,没有用过,以后看机会再补上。
1、ArrayAdapter
ArrayAdapter只能用于显示一行文字,用法一般如下:
mAdapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_spinner_item, getData()); mListView.setAdapter(mAdapter);该类内置add,insert, remove, clear等方法,实现了BaseAdapter没有搞定的4个方法,实现了自动匹配Filterable接口,还提供了两个额外的方法:
1)setDropDownViewResource(int resource):供Spinner使用,设置下拉框显示字符串的View资源样式,也就是一个xml文件,文件里只能定义一个TextView的子类,不能加LinearLayout之类的,要注意。
2)setNotifyOnChange(boolean notifyOnChange):设置为false可以阻止界面知道数据发生了变化,直到外部调用notifyDataSetChanged将mNotifyOnChange开关打开为止;
2、SimpleAdapter
SimpleAdapter相对而言比ArrayAdapter自由很多,我们可以用自己的定义的界面,可以展现很多种数据,包括选中状态、文字、图片等;它的缺点就是数据格式太死板,我们必须使用List<? extends Map<String, ?>>作为数据源,这个转化过程很没有美感,且耗时;
android实现的SimpleAdapter默认只允许待显示对象为Checkable、TextView、ImageView的数据,用法一般如下:
1)布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <CheckBox android:id="@+id/checkBox" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#FF000000" /> </LinearLayout> </LinearLayout>
2)代码
package com.example.testlistview; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; public class MainActivity extends Activity { private ListView mListView = null; private BaseAdapter mAdapter = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); mAdapter = new SimpleAdapter(getApplicationContext(), getData(), R.layout.simple_adapter_demo, new String[] {"text", "image", "checkable"}, new int[] {R.id.textView, R.id.imageView, R.id.checkBox}); mListView.setAdapter(mAdapter); } private List<? extends Map<String, ?>> getData() { ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String,Object>>(); HashMap<String, Object> map1 = new HashMap<String, Object>(); map1.put("text", "nihao"); map1.put("image", android.R.drawable.ic_secure); map1.put("checkable", false); list.add(map1); HashMap<String, Object> map2 = new HashMap<String, Object>(); map2.put("text", "android"); map2.put("image", android.R.drawable.ic_delete); map2.put("checkable", true); list.add(map2); return list; } private void initView() { mListView = (ListView) findViewById(R.id.listview); } }
3) 效果:
当然,SimpleAdapter也不是只能显示这三种数据类型,它有个内部接口ViewBinder,只要自己实现一个ViewBinder的子类,并通过setViewBinder(ViewBinder viewBinder)赋值给SimpleAdapter,我们也可以定义对其他的控件进行赋值。这是SimpleAdapter的扩展性。
不过,说实话瑜不掩瑕,如果数据格式不是Map的,不建议用SimpleAdapter,费脑筋。
六、万能的自定义Adapter
好了,我们为了讲解ListView的适配器Adapter,从Adapter接口往下讲了四层了,到了这里,就是故事的高潮部分。我们讲自定义Adapter,到目前为止,能装在ListView里面的东西只有Checkable、String和Bitmap,这明显不够用,但是android就给了这么点功能,所以我们就得自己动手DIY了。
自定义Adapter,一般都继承BaseAdapter,因为BaseAdapter替我们实现了Adapter的基本功能,包括数据变化通知、部分接口实现等等,虽然ListView的setAdapter方法只要实现了ListAdapter接口的就可以用,但是有现成的,我们干嘛要再实现一遍那十几个接口呢,对吧!~
我们先说一下自定义Adapter的好处:
1)数据存储方式灵活:随便什么格式的,只要自己知道格式,就能往里面放;
2)界面展现方式灵活:你可以任意布局摆弄数据,可以加自定义的分隔符,可以加按钮等等等等;
好了,好处说完了就开始动手了。真写起来,其实自定义Adapter没有啥特别的,继承BaseAdapter,如前文所叙,里面有四个方法是需要我们搞定的,分别是:
1)getCount(),返回传入我们定义的Adapter内的数据列表长度,用于通知ListView绘制多少行。
2)getItem(int position),返回传入的数据列表的第position个数据。
3)getItemId(int position),返回传入的数据列表的第position个数据对应的唯一标识符,强烈建议不要返回position。
4)getView(int position, View convertView, ViewGroup parent),返回传入的数据列表的第position个数据对应在ListView的行内应该怎么绘制,我们甚至可以判断一下position,然后在第三行绘制一个ImageView,展现一个深情款款的大猪头。当然,如果其他各行都不是ImageView的话,我们一定要覆写getItemViewType(int position)和getViewTypeCount(),因为BaseAdapter默认每行类型都是0,总的TypeCount为1。
这里有个两个需要注意的地方:
1)convertView会为当前position带来一个可用的view,是给我们复用的,作用是当滚动导致ListView内某行View不可见时,直接复用该View来展示第position个数据,这样能大大减少内存使用量,不管要显示的数据是不是几千条,一般显示一行字的ListView最多创建10几个View就可以满足显示要求了,所以一定得用上。
2)View有个setTag方法,而我们使用findViewById是要查找View树的,相对而言,创建一个内部类ViewHolder并设为View的Tag,会在复用convertView时直接提供对象引用(如TextView、ImageView等等),所以ViewHolder也应该用上。
下面上代码:
1)布局my_adapter_demo.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" > <CheckBox android:id="@+id/checkBox" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#FF000000" /> </LinearLayout> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="删除" /> </LinearLayout>
2)数据体Data.java
package com.example.testlistview; import android.graphics.Bitmap; public class Data { private int mId = -1; private String mText = null; private Bitmap mImage = null; private boolean mIsChecked = false; public Data(int mId, String mText, Bitmap mImage, boolean mIsChecked) { super(); this.mId = mId; this.mText = mText; this.mImage = mImage; this.mIsChecked = mIsChecked; } public int getId() { return mId; } public void setId(int mId) { this.mId = mId; } public String getText() { return mText; } public void setText(String mText) { this.mText = mText; } public Bitmap getImage() { return mImage; } public void setImage(Bitmap mImage) { this.mImage = mImage; } public boolean isChecked() { return mIsChecked; } public void setIsChecked(boolean mIsChecked) { this.mIsChecked = mIsChecked; } }
3)自定义Adapter MyAdapter.java
package com.example.testlistview; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; public class MyAdapter extends BaseAdapter { private static final String TAG = "MyAdapter"; private List<Data> mList = null; private LayoutInflater mInflater = null; private Context mContext = null; public MyAdapter(Context context, List<Data> list) { if(list == null) { list =new ArrayList<Data>(); } mList = list; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mContext = context; } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int position) { return mList.get(position); } @Override public long getItemId(int position) { //最好返回Data对应的唯一标识id return mList.get(position).getId(); } @Override public View getView(int position, View convertView, ViewGroup parent) { Data data = mList.get(position); View view = null; ViewHolder holder = null; if(null == convertView) { view = mInflater.inflate(R.layout.my_adapter_demo, null); holder = new ViewHolder(); holder.id = data.getId(); holder.textView = (TextView) view.findViewById(R.id.textView); holder.imageView = (ImageView) view.findViewById(R.id.imageView); holder.checkBox = (CheckBox) view.findViewById(R.id.checkBox); holder.button = (Button) view.findViewById(R.id.button); //将持有的控件引用存入View的Tag内 view.setTag(holder); } else { //复用convertView,提高ListView的效率,减少内存占用 view = convertView; //读出控件引用 holder = (ViewHolder) view.getTag(); Log.d(TAG, holder.id + " is now display " + data.getId()); holder.id = data.getId(); } holder.textView.setText(data.getText()); holder.imageView.setImageBitmap(data.getImage()); holder.checkBox.setChecked(data.isChecked()); holder.button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(mContext, "按钮被点击了", Toast.LENGTH_SHORT).show(); } }); return view; } @Override public int getItemViewType(int position) { if(position == 2) { return 1; } return super.getItemViewType(position); } @Override public int getViewTypeCount() { return 2; } private final class ViewHolder { int id = -1; TextView textView = null; ImageView imageView = null; CheckBox checkBox = null; Button button = null; } }
4)主界面 MainActivity.java
package com.example.testlistview; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.os.Bundle; import android.app.Activity; import android.graphics.BitmapFactory; import android.view.Menu; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; public class MainActivity extends Activity { private ListView mListView = null; private BaseAdapter mAdapter = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); mAdapter = new MyAdapter(this, getData()); mListView.setAdapter(mAdapter); } private List<Data> getData() { ArrayList<Data> list = new ArrayList<Data>(); Data data1 = new Data(1, "nihao", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_secure), false); list.add(data1); Data data2 = new Data(2, "android", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_delete), true); list.add(data2); Data data3 = new Data(3, "nihao", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_secure), false); list.add(data3); Data data4 = new Data(4, "android", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_delete), true); list.add(data4); Data data5 = new Data(5, "nihao", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_secure), false); list.add(data5); Data data6 = new Data(6, "android", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_delete), true); list.add(data6); Data data7 = new Data(7, "nihao", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_secure), false); list.add(data7); Data data8 = new Data(8, "android", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_delete), true); list.add(data8); Data data9 = new Data(9, "nihao", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_secure), false); list.add(data9); Data data10 = new Data(10, "android", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_delete), true); list.add(data10); Data data11 = new Data(11, "nihao", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_secure), false); list.add(data11); Data data12 = new Data(12, "android", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_delete), true); list.add(data12); Data data13 = new Data(13, "nihao", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_secure), false); list.add(data13); Data data14 = new Data(14, "android", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_delete), true); list.add(data14); Data data15 = new Data(15, "nihao", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_secure), false); list.add(data15); Data data16 = new Data(16, "android", BitmapFactory.decodeResource(this.getResources(), android.R.drawable.ic_delete), true); list.add(data16); return list; } private void initView() { mListView = (ListView) findViewById(R.id.listview); } }
通过这个例子我们可以验证,不同ViewType的View是不会作为convertView使用的,另外往下滚动时,上方不可见的View会被复用来显示下方将要显示出来的数据。
好了,整个Android的ListView Adapter就介绍到这里,有后续需要注意的地方,我会接着这篇文章往下写的。
相关文章推荐
- Android智能电视应用程序开发浅谈(三)
- Android自定义ListView或GridView适配器Adapter的getView方法不执行的问题
- 浅谈Android开发者2017年最值得关注的25个实用库
- 浅谈在Android中存在的一些内存泄漏
- 浅谈android反调试之 签名校验
- GridView && ArrayAdapter && SimpleAdapter && BaseAdapter【Android】
- android ListView布局之三(使用自定义的Adapter绑定数据,通过contextView.setTag绑定数据)有按钮的ListView
- android的Binder通信机制java层浅谈-android学习之旅(88)
- 浅谈Android系统开发中LOG的使用
- android 的sqlite 主键与simpleCursorAdapter
- android你还在写麻烦的 Adapter么
- Android高手进阶教程(十六)之---Android中万能的BaseAdapter(Spinner,ListView,GridView)的使用!
- Android listview与adapter用法
- Android_Adapter用法总结
- Android SimpleAdapter的参数
- Android Camera OMXCameraAdapter.cpp初始化分析
- Android开发之浅谈Json数据格式
- 浅谈android的selector,背景选择器
- Android之BaseExpandableListAdapter的用法
- 浅谈Android五大布局(一)——LinearLayout、FrameLayout和AbsoulteLayout