ArrayAdapter/SimpleAdapter/BaseAdapter的使用
Android中通过Adapter为AbsListView列表控件提供基础数据,Adapter只是一个接口,它派生了ListAdapter和SpinnerAdapter,其中ListAdapter为AbsListView提供列表,SpinnerAdapter为AbsSpinner提供数据。
![]()
ListView、 Adapter、 数据源三者之间的关系图
ArrayAdapter是较为简单快捷的适配器,不需要创建专门的Item布局来填充列表项,通过提供一个String[]类型的数据源,可以直接通过系统自带的布局文件
String[] list = { "李阳疯狂英语", "AOC", "BBC", "CCTV", "优酷财经" };
// 将数组包装成ArrayAdapter
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context,android.R.layout.simple_list_item_single_choice, list);
listView.setAdapter(adapter);
ArrayAdapter构造函数,第二个参数为 带有一个TextView系统布局资源ID,TextView的ID为@android:id/text1,点击可查看布局如下
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:minHeight="?android:attr/listPreferredItemHeightSmall" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:textAppearance="?android:attr/textAppearanceListItemSmall" />
// 获取resource资源文件定义好的数组
Resources res = getResources();
String[] arrs = res.getStringArray(R.array.books);
// 将数组包装成ArrayAdapter,第二个参数为 带有一个TextView
// 布局资源,TextView的ID为@android:id/text1
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context,android.R.layout.simple_list_item_1, arrs);
listView.setAdapter(adapter);
通过ArrayAdapter实现的Adapter虽然简单、易用,但功能有限,它只能通过每个列表项是TextView,如果需要实现更复杂的列表项,则可以使用SimpleAdapter。SimpleAdapter并非如名字简单,它的功能很强大,可实现ListView的大部分应用。
<?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">
<ImageView android:id="@+id/Img" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/Img" android:gravity="center_vertical" android:layout_marginLeft="20sp"/>
<TextView android:id="@+id/country" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/name" android:layout_marginLeft="20sp"/>
</RelativeLayout>
package com.example.listview03_simpleadapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class MainActivity extends Activity {
private String[] names = { "姚明", "Kobe", "贝克汉姆", "加索尔" };
private String[] country = { "中国", "美国", "英国", "西班牙" };
private int[] imgId = { R.drawable.j1a2, R.drawable.j1a4, R.drawable.j1a5,R.drawable.j1a6 };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建一个List集合,集合元素Map
List<Map<String, Object>> listItem = new ArrayList<Map<String, Object>>();
for (int i = 0; i < names.length; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("img", imgId[i]);
map.put("name", names[i]);
map.put("country", country[i]);
listItem.add(map);
}
// 创建Adapter实例
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this, listItem,R.layout.item, new String[] { "img", "name", "country" },new int[] { R.id.Img, R.id.name, R.id.country });
ListView listView = (ListView) findViewById(R.id.listViewId);
listView.setAdapter(adapter);
}
}
【重点】下面重点介绍,BaseAdapter是一个实现了ListAdapter和SpinnerAdapter接口的抽象类,可用于为ListView和Spinner提供数据适配器。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
关于BaseAdapter的使用,相信一张图足以说明
[图片来源:鸿洋大神的博客]
(http://blog.csdn.net/lmj623565791/article/details/44014941)
public class MyListAdapter extends BaseAdapter {
private Context context;
/** 数据源 */
private List<User> mList;
private LayoutInflater inflater;
public MyListAdapter(Context context, List<User> mList) {
super();
this.context = context;
this.mList = mList;
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return mList.size();
}
@Override
public User getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
从上代码片段中可以看到,我们自定义的数据适配器继承子BaseAdapter,需要复写下面四个方法
getCount() —获取数据源个数
getItem(int position) —获取指定position的数据实体
getItemId(int position) —获取数据列表的行编号,一般返回position
getView(int position, View convertView, ViewGroup parent) —此方法最为重要,返回一个显示在数据列表中View;
在getView方法加载指定XML布局文件,设置每个Item显示内容,绑定Item中各个控件的监听事件,具体操作以下代码块
首先,getView的三个参数分别代表: 序号(位置)、列表中一个ITEM的显示视图、view依附的父视图
Android中有个叫做Recycler(反复循环器)的构件,下图是它的工作原理:
convert的复用:一共有200条,每屏可以显示7条数据,初次加载并不会一次性调用200次getView方法,只会调用前7次,生成7个convertView显示到页面上, 当页面滑动第一个Item被划出屏幕外,此时划出屏幕外的Item1存在convertView中,convertView是一个xml资源生成的View,当底部有新的Item划入屏幕,此时convertView持有了划出屏幕外的view,可以拿来直接使用,而减少了inflate资源的次数。也就是说利用convertView的复用,虽然数据源总个数为200,但只有第一次 执行inflate加载xml资源。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = inflater.inflate(R.layout.item_simple, parent, false);
}
TextView tvName = (TextView) view.findViewById(R.id.name);
TextView tvCity = (TextView) view.findViewById(R.id.country);
tvName.setText(mList.get(position).getName());
tvCity.setText(mList.get(position).getCountry());
Log.i("getView", "pos:" + position);
return view;
}
从上述代码片中,通过convertView复用减少了inflate加载资源的次数,可每次执行getView方法时,每次通过findViewById去找convertView的所用子控件,在反复滑动时会不断调用getView方法,所以同时就会反复调用findViewById方法。为了提高性能,减少findViewById次数,我们这里就用到了ViewHolder来持有convertView的每一个子控件,以setTag的形式和convertView绑定,需要时再从convertView.getTag()中取出,避免了反复findViewById的情况。
private class ViewHolder {
public TextView tvName;
public TextView tvCity;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
ViewHolder viewHolder = new ViewHolder();
view = inflater.inflate(R.layout.item_simple, parent, false);
// 通过ViewHolder持有view中的子控件
viewHolder.tvName = (TextView) view.findViewById(R.id.name);
viewHolder.tvCity = (TextView) view.findViewById(R.id.country);
// 再通过setTag的形式和view绑定
view.setTag(viewHolder);
}
ViewHolder viewHolder = (ViewHolder) view.getTag();
viewHolder.tvName.setText(mList.get(position).getName());
viewHolder.tvCity.setText(mList.get(position).getCountry());
return view;
}