参考资料:

  • 《Android 编程权威指南》

ListFragment

ListFragment 是 Fragment 的子类,其内置列表显示支持功能。它的编写方法和普通的 Fragment 类似。示例:

1
2
3
4
5
6
7
8
9
10
11
public class CrimeListFragment extends ListFragment {
private ArrayList<Crime> mCrimes;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
mCrimes = CrimeLab.get(getActivity()).getCrimes();
}
}

其中,mCrimes 是要被展示的对象列表。注意查看 getActivity() 方法。该 Fragment 便利方法不仅可以返回托管 Activity ,且允许 fragment 处理更多的 activity 相关事务。

ListView

ListView 是 ViewGroup 的子类,每一个列表项都是作为 ListView 的一个 View 子对象显示的。这些 View 子对象既可以是复杂的 View 对象,也可以是简单的 View 对象,这取决于我们对列表显示复杂度的需要。

ListView 是从 adapter 申请视图对象的。adapter 是一个控制器对象,从模型层获取数据,并将之提供给 ListView 显示,起到了沟通桥梁的作用。

adapter 负责:

  • 创建必要的视图对象;
  • 用模型层数据填充视图对象;
  • 将准备好的视图对象返回给 ListView 。

adapter 是实现 Adapter 接口的类实例。例如,ArrayAdapter 类知道如何处理数组(或 ArrayList)中的 T 类型对象。下图展示了 ArrayAdapter 类的继承图谱。继承图谱的每一个节点都提供了该层级类或接口的专业化形式。

ListView 需要显示视图对象时,会与其 adapter 展开会话沟通。下图展示了 ListView 向其数组 adapter 启动会话的例子。

创建 Adapter

下面以 ArrayAdapter 为例学习如何编写 adapter 。

首先,使用以下构造方法创建一个默认的 ArrayAdapter 类实现:

1
public ArrayAdapter(Context context, int textViewResourceId, T[] objects)

数组 adapter 构造方法的第一个参数是一个 Context 对象,使用第二个参数的资源 ID 需要该 Context 对象。资源 ID 可定位 ArrayAdapter 用来创建 View 对象的布局。第三个参数是数据集(要展示的数组对象)。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
mCrimes = CrimeLab.get(getActivity()).getCrimes();
ArrayAdapter<Crime> adapter =
new ArrayAdapter<Crime>(getActivity(),
android.R.layout.simple_list_item_1,
mCrimes);
setListAdapter(adapter);
}

另外要注意的一点是,ArrayAdapter<T>.getView(...) 实现方法依赖于 toString() 方法。因此,一定要确保被显示的对象提供了合理的 toString() 方法。例如,可以覆盖该被显示对象的 toString() 使其返回该对象的标题属性:

1
2
3
4
@Override
public String toString() {
return mTitle;
}

响应列表项的点击事件

要响应用户对列表项的点击事件,可覆盖 ListFragment 类的另一便利方法:

1
public void onListItemClick(ListView l, View v, int position, long id)

示例:

1
2
3
4
5
6
7
8
9
10
11
public class CrimeListFragment extends ListFragment {
private static final String TAG = "CrimeListFragment";
...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Crime c = (Crime)(getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + "was clicked");
}
}

定制列表项

定制列表项需完成以下任务:

  • 创建定义列表项视图的 XML 布局文件;
  • 创建 ArrayAdapter 的子类,用来创建、填充并返回定义在新布局中的视图。

定制布局用来显示特定 Crime 对象信息的列表项。列表项要显示的数据信息必须使用 Crime 类的 getter 方法才能获取,因此,我们需创建一个知道如何与 Crime 对象交互的 adapter 。例如可以在 ListFragment 类中创建一个 ArrayAdapter 的子类作为内部类。

1
2
3
4
5
private class CrimeAdapter extends ArrayAdapter<Crime> {
public CrimeAdapter(ArrayList<Crime> crimes) {
super(getActivity(), 0, crimes);
}
}

创建并返回定制列表项是在以下 ArrayAdapter 方法里实现的:

1
public View getView(int position, View convertView, ViewGroup parent)

convertView 参数是一个已存在的列表项,adapter 可重新配置并返回它,因此我们无需再创建全新的视图对象。复用视图对象可避免反复创建、销毁同一类对象的开销,应用性能因此得到了提高。ListView 一次性需显示大量列表项,因此,没有理由产生大量暂不使用的视图对象来耗尽宝贵的内存资源。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private class CrimeAdapter extends ArrayAdapter<Crime> {
public CrimeAdapter(ArrayList<Crime> crimes) {
super(getActivity(), 0, crimes);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// If weren't given a view, inflate one
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.list_item_crime, null);
}
// Configure the view for this Crime
Crime c = getItem(position);
TextView titleTextView =
(TextView)convertView.findViewById(R.id.crime_list_item_titleTextView);
titleTextView.setText(c.getTitle());
TextView dateTextView =
(TextView)convertView.findViewById(R.id.crime_list_item_dateTextView);
dateTextView.setText(c.getDate().toString());
CheckBox solvedCheckBox =
(CheckBox)convertView.findViewById(R.id.crime_list_item_solvedCheckBox);
solvedCheckBox.setChecked(c.isSovled());
return convertView;
}
}

最后需要在 ListFragment 类绑定定制的 adapter ,更新 onCreate()onListItemClick() 方法以实现 CrimeAdapter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
mCrimes = CrimeLab.get(getActivity()).getCrimes();
CrimeAdapter adapter = new CrimeAdapter(mCrimes);
setListAdapter(adapter);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Crime c = ((CrimeAdapter)(getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + "was clicked");
}

Comments