ListView的三大适配器ArrayAdapter,SimpleAdapter,MyAdapter
2015-04-16 18:18
246 查看
ArrayAdapter
是一个只在列表项ListItem上显示TextView的适配器。
(其实在列表项上可以有其他组件,在后面会介绍)我们来看一下其构造函数。
ArrayAdapter arrayAdapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
在android.R.layout.simple_list_item_1中只有一个TextView,连容器LinearLayout都没有,且也不能有。为什么呢?看看源码。
首先在init(...)函数中,将android.R.layout.simple_list_item_1赋值给mResource。
我们看一下在ListView获取每个列表项Item的getView函数中,是如何得到这个列表项View的。
我们来看一下这个构造函数。
第二个参数resource的含义是布局文件的id,和我们上面的android.R.layout.simple_list_item_1是类似的,第三个参数textViewResourceId是该布局文件中的TextView的id。
首先我们定义一个layout布局文件layout_simple_list,在该布局文件中可以有其他控件。
总的来说,ArrayAdapter是最简单的ListView的适配器,内部只有一个TextView,而且参数我们一般使用android内部的id变量android.R.layout.simple_list_item_1。
SimpleAdapter
是android提供的向ListView添加复杂项的适配器,但其仍然是有局限性的,SimpleAdapter只支持3种组件:
1.实现Checkable接口的组件类。
2.TextView类及其子类。
3.ImageView类及其子类。
我们首先看一下SimpleAdapter的使用方法,再分析其内部的实现。
既然是添加复杂的列表项,那么肯定需要先提供一个xml布局文件,这个文件做为每一个列表项的布局模板。
layout_list_item.xml文件中代码如下:
然后因为我们的列表项中组件为TextView和ImageView,我们需要根据其组件id对其进行赋值(类型是字符串或者图片id)。
所以我们需要一个存放组件id的数组,来索引到指定的组件。
int[] to=new int[]{R.id.text,R.id.image};
通过to数组的第i项可以得到组件的id,同样我们需要一个与之对应的存放具体值的数组。
from=new String[]{"Text Index","Image Index"};
to数组与from数组是一一对应的,from数组中存放的是一个key,根据这个key值,我们到Map中去取相应的具体的值。
来看一下SimpleAdapter内部是如何实现得到每个列表项View及给列表项中的组件赋值的。
= mData.get(position);先得到List中存放的键值对Map.
final Object data = dataSet.get(from[i]);再得到具体的值
final View v = view.findViewById(to[i]);利用to数组中存放的id获到组件,
接着分三种情况来讨论组件的类型,Checkable,TextView,ImageView分别进行赋值。其实这样也给我们提供了自定义适配器的思路。
可以看出SimpleAdapter可以支持的组件是有限的,更别说我们自定义的一些组件了。
为了实现给ListView添加更复杂的列表项,这是就需要我们来自定义适配器了。
自定义的适配器是继承自BaseAdapter的。
其实我们之前分析的ArrayAdapter和SimpleAdapter都是继承自BaseAdapter的。
即使上面的两种适配器中也可以添加Button组件,但是也只能进行数据填充,Button无法进行点击事件响应。下面我们通过自定义适配器MyAdapter来实现列表项中的Button动态删除列表项自身。
首先看一下列表项的布局模板文件
我们仍然需要一个list来存放组件的具体数据。
是一个只在列表项ListItem上显示TextView的适配器。
(其实在列表项上可以有其他组件,在后面会介绍)我们来看一下其构造函数。
public ArrayAdapter(Context context, int textViewResourceId) { init(context, textViewResourceId, 0, new ArrayList<T>()); } public ArrayAdapter(Context context, int resource, int textViewResourceId) { init(context, resource, textViewResourceId, new ArrayList<T>()); } public ArrayAdapter(Context context, int textViewResourceId, T[] objects) { init(context, textViewResourceId, 0, Arrays.asList(objects)); } public ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) { init(context, resource, textViewResourceId, Arrays.asList(objects)); } public ArrayAdapter(Context context, int textViewResourceId, List<T> objects) { init(context, textViewResourceId, 0, objects); } public ArrayAdapter(Context context, int resource, int textViewResourceId, List<T> objects) { init(context, resource, textViewResourceId, objects); }可以看到最终调用的都是init(...)函数。
private void init(Context context, int resource, int textViewResourceId, List<T> objects) { mContext = context; mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mResource = mDropDownResource = resource; mObjects = objects; mFieldId = textViewResourceId; }1.使用android的布局文件android.R.layout.simple_list_item_1。
ArrayAdapter arrayAdapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
在android.R.layout.simple_list_item_1中只有一个TextView,连容器LinearLayout都没有,且也不能有。为什么呢?看看源码。
首先在init(...)函数中,将android.R.layout.simple_list_item_1赋值给mResource。
我们看一下在ListView获取每个列表项Item的getView函数中,是如何得到这个列表项View的。
public View getView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mResource); }来分析一下,下面这段代码的执行,这里mFieldId是等于0的,因为构造函数中的初始化,那么就会执行text = (TextView) view;view的来源是view = mInflater.inflate(resource, parent, false);可以看出,通过inflate布局文件得到view,而这个view又赋值给了TextView。那么在布局文件中肯定只能有一个TextView了。那可不可以有其他控件呢?当然是可以的,那就要是调用其他构造函数了。
</pre><pre name="code" class="java">private View createViewFromResource(int position, View convertView, ViewGroup parent, int resource) { View view; TextView text; if (convertView == null) { view = mInflater.inflate(resource, parent, false); } else { view = convertView; } try { if (mFieldId == 0) { // If no custom field is assigned, assume the whole resource is a TextView text = (TextView) view; } else { // Otherwise, find the TextView field within the layout text = (TextView) view.findViewById(mFieldId); } } catch (ClassCastException e) { Log.e("ArrayAdapter", "You must supply a resource ID for a TextView"); throw new IllegalStateException( "ArrayAdapter requires the resource ID to be a TextView", e); } T item = getItem(position); if (item instanceof CharSequence) { text.setText((CharSequence)item); } else { text.setText(item.toString()); } return view; }
我们来看一下这个构造函数。
public ArrayAdapter(Context context, int resource, int textViewResourceId) { init(context, resource, textViewResourceId, new ArrayList<T>()); }
第二个参数resource的含义是布局文件的id,和我们上面的android.R.layout.simple_list_item_1是类似的,第三个参数textViewResourceId是该布局文件中的TextView的id。
首先我们定义一个layout布局文件layout_simple_list,在该布局文件中可以有其他控件。
<?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="match_parent" android:orientation="horizontal"> <TextView android:id="@+id/simple_list_item_text" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button"/> </LinearLayout>既有LinearLayout,还有一个Button控件。那么第三个参数就是文件中的TextView的id:simple_list_item_text。我们来分析调用以下构造函数后的执行过程:
arrayAdapter=new ArrayAdapter<String>(this,R.layout.layout_simple_list,R.id.simple_list_item_text);在init(...)的执行中,mResource=R.layout.layout_simple_list,mFieldId=R.id.simple_list_item_text。那么在createViewFromResource(...)函数中执行的是
view = mInflater.inflate(resource, parent, false);
text = (TextView) view.findViewById(mFieldId);这里就可以直观的看出ArrayAdapter内部在得到每一个列表项View时,是通过先实例化layout布局,再从布局中获取TextView控件,所以说布局中有无其他控件是没关系的,但是ArrayAdapter只负责对每个列表项中的TextView进行数据填充。
总的来说,ArrayAdapter是最简单的ListView的适配器,内部只有一个TextView,而且参数我们一般使用android内部的id变量android.R.layout.simple_list_item_1。
SimpleAdapter
是android提供的向ListView添加复杂项的适配器,但其仍然是有局限性的,SimpleAdapter只支持3种组件:
1.实现Checkable接口的组件类。
2.TextView类及其子类。
3.ImageView类及其子类。
我们首先看一下SimpleAdapter的使用方法,再分析其内部的实现。
既然是添加复杂的列表项,那么肯定需要先提供一个xml布局文件,这个文件做为每一个列表项的布局模板。
layout_list_item.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="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
然后因为我们的列表项中组件为TextView和ImageView,我们需要根据其组件id对其进行赋值(类型是字符串或者图片id)。
所以我们需要一个存放组件id的数组,来索引到指定的组件。
int[] to=new int[]{R.id.text,R.id.image};
通过to数组的第i项可以得到组件的id,同样我们需要一个与之对应的存放具体值的数组。
from=new String[]{"Text Index","Image Index"};
to数组与from数组是一一对应的,from数组中存放的是一个key,根据这个key值,我们到Map中去取相应的具体的值。
list=new ArrayList<Map<String,Object>>(); for(int i=0;i<5;i++){ map=new HashMap<String,Object>(); map.put("Text Index", "This is TextView"+i); map.put("Image Index", R.drawable.ic_launcher); list.add(map); }list中存放的是每一个列表项的Map,Map中有存放了列表项的每个组件的具体的值。我们就是根据from中存放的索引来得到Map中存放的具体值,然后在将其赋给通过to数组中的值得到的组件。
来看一下SimpleAdapter内部是如何实现得到每个列表项View及给列表项中的组件赋值的。
public SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) { mData = data; mResource = mDropDownResource = resource; mFrom = from; mTo = to; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); }构造函数中将我们存放键值对的Map的List赋给mData,mResource为我们的列表项组件布局模板,
public View getView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mResource); } private View createViewFromResource(int position, View convertView, ViewGroup parent, int resource) { View v; if (convertView == null) { v = mInflater.inflate(resource, parent, false); } else { v = convertView; } bindView(position, v); return v; }getView在每次添加列表项是调用,首先根据布局文件来实例化View。
private void bindView(int position, View view) { final Map dataSet = mData.get(position); if (dataSet == null) { return; } final ViewBinder binder = mViewBinder; final String[] from = mFrom; final int[] to = mTo; final int count = to.length; for (int i = 0; i < count; i++) { final View v = view.findViewById(to[i]); if (v != null) { final Object data = dataSet.get(from[i]); String text = data == null ? "" : data.toString(); if (text == null) { text = ""; } boolean bound = false; if (binder != null) { bound = binder.setViewValue(v, data, text); } if (!bound) { if (v instanceof Checkable) { if (data instanceof Boolean) { ((Checkable) v).setChecked((Boolean) data); } else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the bottom of these // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text); } else { throw new IllegalStateException(v.getClass().getName() + " should be bound to a Boolean, not a " + (data == null ? "<unknown type>" : data.getClass())); } } else if (v instanceof TextView) { setViewText((TextView) v, text); } else if (v instanceof ImageView) { if (data instanceof Integer) { setViewImage((ImageView) v, (Integer) data); } else { setViewImage((ImageView) v, text); } } else { throw new IllegalStateException(v.getClass().getName() + " is not a " + " view that can be bounds by this SimpleAdapter"); } } } } }final Map dataSet
= mData.get(position);先得到List中存放的键值对Map.
final Object data = dataSet.get(from[i]);再得到具体的值
final View v = view.findViewById(to[i]);利用to数组中存放的id获到组件,
接着分三种情况来讨论组件的类型,Checkable,TextView,ImageView分别进行赋值。其实这样也给我们提供了自定义适配器的思路。
可以看出SimpleAdapter可以支持的组件是有限的,更别说我们自定义的一些组件了。
为了实现给ListView添加更复杂的列表项,这是就需要我们来自定义适配器了。
自定义的适配器是继承自BaseAdapter的。
其实我们之前分析的ArrayAdapter和SimpleAdapter都是继承自BaseAdapter的。
即使上面的两种适配器中也可以添加Button组件,但是也只能进行数据填充,Button无法进行点击事件响应。下面我们通过自定义适配器MyAdapter来实现列表项中的Button动态删除列表项自身。
首先看一下列表项的布局模板文件
<?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="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="删除"/> </LinearLayout>在这个布局中简单的添加了三个组件:TextView,ImageView,Button。
我们仍然需要一个list来存放组件的具体数据。
public List<Map<String,Object>>mData; mData=new ArrayList<Map<String,Object>>(); Map<String,Object>map1=new HashMap<String,Object>(); map1.put("text", "测试数据1"); map1.put("image", R.drawable.pic1); map1.put("button", "按钮1"); mData.add(map1); Map<String,Object>map2=new HashMap<String,Object>(); map2.put("text", "测试数据2"); map2.put("image", R.drawable.pic2); map2.put("button", "按钮2"); mData.add(map2); Map<String,Object>map3=new HashMap<String,Object>(); map3.put("text", "测试数据3"); map3.put("image", R.drawable.pic3); map3.put("button", "按钮3"); mData.add(map3);在使用上简单的创建适配器对象,然后对ListView设置适配器。
MyAdapter myAdapter=new MyAdapter(this); ListView listView5.setAdapter(myAdapter);然后我们来分析一下MyAdapter的实现。
class ViewHolder{ TextView text; ImageView image; Button button; }我们需要一个ViewHolder来管理列表项中的组件集合。
class MyAdapter extends BaseAdapter{ private Context mContext; private LayoutInflater inflater; MyAdapter(Context context){ mContext=context; inflater=LayoutInflater.from(context); } @Override public int getCount() { if(mData!=null){ return mData.size(); } else return 0; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(final int position, View convertView, ViewGroup parent) { if(convertView==null){ View v=inflater.inflate(R.layout.layout_myadapter, parent, false); ViewHolder holder=new ViewHolder(); holder.text=(TextView)v.findViewById(R.id.text); holder.image=(ImageView)v.findViewById(R.id.image); holder.button=(Button)v.findViewById(R.id.button); holder.text.setText((CharSequence) mData.get(position).get("text")); holder.image.setImageResource((Integer) mData.get(position).get("image")); holder.button.setText((CharSequence) mData.get(position).get("button")); holder.button.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { Toast.makeText(mContext, (CharSequence) mData.get(position).get("button"), Toast.LENGTH_SHORT).show(); mData.remove(position); notifyDataSetChanged(); } }); v.setTag(holder); return v; } else{ ViewHolder holder=(ViewHolder)convertView.getTag(); holder.text.setText((CharSequence) mData.get(position).get("text")); holder.image.setImageResource((Integer) mData.get(position).get("image")); holder.button.setText((CharSequence) mData.get(position).get("button")); } return convertView; } }列表项的个数是通过执行getCount函数得到的,也就是mData的大小。我们可以通过删除该List中指定的项来删除指定的列表项。在ListView得到每个列表项的getView函数中,使用了convertView。为什么不是直接生成View呢?假如我们的列表中有1000项,首先肯定不是同时全部生成的,一般是当列表滑动到需要显示时才会生成,那么当该列表项处于显示状态时,convertView==null,当我们滑动列表导致该列表项不可见时,系统并不一定会立即销毁它,而是保存到convertView,所以我们一般先检查convertView是否为空,若不为空,我们则直接使用该convertView,若为空,我们再创建新的view。这里我们将ViewHolder保存到view的Tag,再通过该viewholder来管理组件。也可以看到我们对ViewHolder的Button添加了事件监听,可以动态删除列表项。
相关文章推荐
- 三大适配器ArrayAdapter,SimpleAdapter,MyAdapter
- ListView 的三种适配器 BaseAdapter ArrayAdapter SimpleAdapter(3)
- ListView (3) 之适配器 ArrayAdapter/SimpleAdapter/BaseAdapter
- ListView 的三种适配器 BaseAdapter ArrayAdapter SimpleAdapter(1)
- Android中ListView与适配器ArrayAdapter,SimpleAdapter,定义BaseAdapter适配器,ListView添加headerView,ListView滚动到最后一项
- ListView 的三种适配器 BaseAdapter ArrayAdapter SimpleAdapter(2)
- Android UI设计:ListView适配器——ArrayAdapter、SimpleAdapter与BaseAdapter
- ArrayAdapter做的listview适配器
- [Android] Adapter:SimpleAdapter SimpleCursorAdapter ArrayAdapter 与ListView的用法
- 用SimpleAdapter做的listview适配器
- Android UI 设计:ListView 控件与 SimpleAdapter 适配器
- Listview(下拉刷新,第三方插件XMLListView、PulltoRefresh),SimpleAdapter、BaseAdapter(适配器),优化,AsyncTask(异步任务,后台刷新)
- ListView适配器--ArrayAdapter;SimpleAdapter;SimpleCursorAdapter
- 安卓:将数据库中的数据查询出来用adapter(SimpleAdapter,SimpleCursorAdapter,自定义适配器CursorAdapter)绑定到listview上
- Android适配器之ArrayAdapter、SimpleAdapter和BaseAdapter的简单用法与有用代码片段
- Android适配器之ArrayAdapter、SimpleAdapter和BaseAdapter的简单用法与有用代码片段
- 【Android】Android适配器之ArrayAdapter、SimpleAdapter和BaseAdapter的简单用法
- Android适配器之ArrayAdapter、SimpleAdapter和BaseAdapter的简单用法与有用代码片段
- [转]Android适配器之ArrayAdapter、SimpleAdapter和BaseAdapter的简单用法与有用代码片段
- Android中ListView常用的两种适配器ArrayAdapter和SimpleAdapter使用