ListView是比较常用的数据展示控件,当展示的数据包含多种控件并且各个控件需要根据数据采取不同行为时,系统自带Adapter就显得不够用,因此本文主要介绍自定义ListView中各个Item行为的基本步骤。
首先分析ListView的基本行为。
正常使用ListView需要两步:
1、布局中添加ListView,一般是在xml文件中就添加好,理论上动态添加也是可以的
2、为该ListView指定一个Adapter
关键在于指定Adapter,在Adapter中可以实现对Item的大部分操作。
先看安卓自带的Adapter类SimpleAdapter
public class SimpleAdapter extends BaseAdapter implements Filterable注:BaseAdapter是基类,我们自己写的Adapter也需要继承BaseAdapter,Filterable是SimpleAdapter自己的需要,不用管。
稍微分析Adapter,继承BaseAdapter后,要求实现
@Override public int getCount() @Override public Object getItem(int position) @Override public long getItemId(int position) @Override public View getView(int position, View convertView, ViewGroup parent)
getCount()、getItem()、getItemId()直接参考SimpleAdapter中的对应方法即可,这3个方法可以理解为当需要获取ListView中的Item信息时,就会调用这3个方法。
关键实现在于getView(),假设你的ListView中有5个Item,那么就会执行5次getView()生成5个Item实例。因此在getView()里面你可以对Item里面的各个控件进行具体的赋值或者状态设置。
要对Item控件进行操作,需要获取相关的参数,同样参考SimpleAdapter的构造方法
/** * Constructor * * @param context The context where the View associated with this SimpleAdapter is running * @param data A List of Maps. Each entry in the List corresponds to one row in the list. The * Maps contain the data for each row, and should include all the entries specified in * "from" * @param resource Resource identifier of a view layout that defines the views for this list * item. The layout file should include at least those named views defined in "to" * @param from A list of column names that will be added to the Map associated with each * item. * @param to The views that should display column in the "from" parameter. These should all be * TextViews. The first N views in this list are given the values of the first N columns * in the from parameter. */ public SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)简单解释:
context:因为需要创建Item视图,所以需要知道Context
data:根据数据来动态显示内容,List对应ListView,因此Map对应每个Item
resource:指定Item的Layout,通过xml构建Item,理论上在代码中构建也是可以
from:Map中的Key,在SimpleAdapter里面是根据顺序对应关系来给控件赋值的
to:每个控件的id,顺序对应from,根据id取控件实例再操作。
事实上不一定要与SimpleAdapter的构造方法一致,只要我们的得到所需要的值就可以了。分别是:
布局需要的Context,展示需要的data,Item中每个控件的Id,Item的layout
至于如何实现参数传递就是我们自己的事了。
假设必要的数据都已经能拿到,再看SimpleAdapter中的getView()的关键实现
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()调用了createViewFromResource(),所以看createViewFromResource();
if (convertView == null) { v = mInflater.inflate(resource, parent, false); } else { v = convertView; }这里为什么需要判断convertView是否为空是因为,有可能有Item实例超出ListView的可见区域,当这个Item的View不可见了,就会被传到这里,可以实现重用而不需要每个Item都新建一个View实例造成资源浪费。假设你有100个Item,但是用户可见的只有20个,那么我们需要的View实际上只需要20个。
所以这里当convertView为空时才通过Inflater创建新View。
注:inflate(resource, parent, false);中false不设置会出错,具体原因没研究。
最后一步,对最后得到View(即Item)进行赋值等操作,SimpleAdapter的具体实现在bindView()中操作。
根据之前传进来的控件id,通过父视图的findViewById()方法获取对应控件实例,得到控件实例后,想怎么做都可以了。