ArrayAdapter
是一个只在列表项ListItem上显示TextView的适配器。
(其实在列表项上可以有其他组件,在后面会介绍)我们来看一下其构造函数。
public ArrayAdapter(Context context, int textViewResourceId) {
init(context, textViewResourceId, 0, new ArrayList());
}
public ArrayAdapter(Context context, int resource, int textViewResourceId) {
init(context, resource, textViewResourceId, new ArrayList());
}
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 objects) {
init(context, textViewResourceId, 0, objects);
}
public ArrayAdapter(Context context, int resource, int textViewResourceId, List objects) {
init(context, resource, textViewResourceId, objects);
}
可以看到最终调用的都是init(...)函数。 private void init(Context context, int resource, int textViewResourceId, List 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
在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了。那可不可以有其他控件呢?当然是可以的,那就要是调用其他构造函数了。
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());
}
首先我们定义一个layout布局文件layout_simple_list,在该布局文件中可以有其他控件。
既有LinearLayout,还有一个Button控件。那么第三个参数就是文件中的TextView的id:simple_list_item_text。我们来分析调用以下构造函数后的执行过程:
arrayAdapter=new ArrayAdapter(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文件中代码如下:
list=new ArrayList
list中存放的是每一个列表项的Map,Map中有存放了列表项的每个组件的具体的值。我们就是根据from中存放的索引来得到Map中存放的具体值,然后在将其赋给通过to数组中的值得到的组件。public SimpleAdapter(Context context, List extends Map> 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 ? "" : 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.为了实现给ListView添加更复杂的列表项,这是就需要我们来自定义适配器了。
自定义的适配器是继承自BaseAdapter的。
其实我们之前分析的ArrayAdapter和SimpleAdapter都是继承自BaseAdapter的。
即使上面的两种适配器中也可以添加Button组件,但是也只能进行数据填充,Button无法进行点击事件响应。下面我们通过自定义适配器MyAdapter来实现列表项中的Button动态删除列表项自身。
首先看一下列表项的布局模板文件
在这个布局中简单的添加了三个组件:TextView,ImageView,Button。
我们仍然需要一个list来存放组件的具体数据。
public List
在使用上简单的创建适配器对象,然后对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添加了事件监听,可以动态删除列表项。