Android 控件之 ListView
ListView 是 Android 系统中最常使用的的控件之一——因为手机屏幕的面积有限,很难显示足够的内容,所以需要滚动显示。ListView 允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕中,同时屏幕中已有的内容则会滚出屏幕。
通常来说,ListView 的使用应该包含以下几个步骤:
- 在 layout 中准备需要使用 ListView 的地方
- 准备需要在 ListView 中显示的数据
- 准备适配器(包括上下文,使用的样式,要填充的数据)
- 将适配器装载入 ListView
ArrayAdapter adapter = new ArrayAdapter(Activity.this, android.R.layout.simple_list_item_1, data);
listView.setAdapter(adapter);
其中的 Activity.this
指定了上下文,android.R.layout.simple_list_item_1
指定了要使用的子项布局 id,最后一项 data 就是要加载的数据。
准备好适配器后,直接使用 ListView 的 setAdapter
方法加载适配器即可。
适配器界面的定制
要定制一个适配器,一般分为以下几个步骤:
- 定义一个类作为适配器的适配类型
- 准备一个想要的布局
- 自定义一个适配器类,且在其中实现
getView
方法 - 准备适配器(包括上下文,使用的样式,要填充的数据)
- 将适配器装载入 ListView
public class FruitAdapter extends ArrayAdapter {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int postion, View convertView, ViewGroup parent){
Fruit fruit = getItem(postion);
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
...
return view;
}
}
在以上的代码中,首先重写了父类的构造函数,将 ListView 的子项布局传递进来。然后重写了 getView 方法,这个方法在每一个 ListView 子项从屏幕外滚动到屏幕内时都会被调用。在这个方法中,首先调用 getItem 方法获得当前数据的实例,然后使用 LayoutInflater 为这个子项加载传入的布局。
LayoutInflater 的 inflate() 方法接收 3 个参数,第一个参数是上下文,第二个参数是要使用的布局 id,第三个参数 false 是指表示只让我们在父布局中声明的 layout 属性生效,单是不为这个 view 添加父布局。因为 view 一旦拥有父布局以后,就再不能添加到 ListView 中。
提升效率
按照之前的方法,每当加载一个子项时,都需要重新加载一次布局。getView 方法中的 convertView 参数,就可以用于解决这一问题:
public class FruitAdapter extends ArrayAdapter {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int postion, View convertView, ViewGroup parent){
Fruit fruit = getItem(postion);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
} else {
view = convertView
}
... = view.finViewById(...);
return view;
}
}
在代码中首先判断视图是否存在,如果不存在,则新建布局,否则直接加载已有的布局。
通过以上代码,可以解决每次都需要重新加载视图的问题,单是依旧需要每次使用 findViewById 方法获取控件的实例。可以借助 ViewHolder 方法优化:
public class FruitAdapter extends ArrayAdapter {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int postion, View convertView, ViewGroup parent){
Fruit fruit = getItem(postion);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.... = view.findViewById(...);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = view.getTag();
}
... = view.finViewById(...);
return view;
}
}
首先,准备一个 ViewHolder,当需要新建 view 的时候,就将 view 中的组件通过 setTag 方法将 viewHolder 存入该 view 中。
ListView 中的点击事件
通过设置 ListView 对象的 setOnItemClickListener 方法可以实现 ListView 子项的点击:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
......
}
});