//此系列博文是《第一行Android代码》的学习笔记,如有错漏,欢迎指正!
ListView 这个控件比较复杂, 就是因为它有很多的细节可以优化,下面我们在试试提高它的运行效率:
一、提高ListView的运行效率:
目前我们ListView的运行效率是很低的, 因为在LetterAdapter的getView()方法中每次都将布局重新加载了一遍,当 ListView快速滚动的时候这就会成为性能的瓶颈。
仔细观察的话,可以发现getView()方法中还有一个 convertView 参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。
1 @Override
2 public View getView(int position, View convertView, ViewGroup parent) {
3 Letter letter = getItem(position); // 获取当前项的Letter实例
4 View view;
5 if (convertView == null) {
6 view = LayoutInflater.from(getContext()).inflate(resourceId, null);
7 } else {
8 view = convertView;
9 }
10 ImageView letterImage = (ImageView) view.findViewById(R.id.letter_image);
11 TextView letterName = (TextView) view.findViewById(R.id.letter_name);
12 letterImage.setImageResource(letter.getImageId());
13 letterName.setText(letter.getName());
14 return view;
15 }
可以看到,现在我们在 getView()方法中进行了判断,如果 convertView 为空,则使用LayoutInflater 去加载布局,如果不为空则直接对 convertView 进行重用。这样就大大提高了ListView的运行效率,在快速滚动的时候也可以表现出更好的性能。此外,我们还可以继续优化:
1 public class LetterAdapter extends ArrayAdapter<Letter> {
2 private int resourceId;
3 public LetterAdapter(Context context, int textViewResourceId, List<Letter> objects) {
4 super(context, textViewResourceId, objects);
5 resourceId = textViewResourceId;
6 }
7 @Override
8 public View getView(int position, View convertView, ViewGroup parent) {
9 Letter letter = getItem(position); // 获取当前项的Letter实例
10 View view;
11 ViewHolder viewHolder;
12 if (convertView == null) {
13 view = LayoutInflater.from(getContext()).inflate(resourceId, null);
14 viewHolder = new ViewHolder();
15 viewHolder.letterImage = (ImageView) view.findViewById
16 (R.id.letter_image);
17 viewHolder.letterName = (TextView) view.findViewById
18 (R.id.letter_name);
19 view.setTag(viewHolder); // 将ViewHolder 存储在View
20 } else {
21 view = convertView;
22 viewHolder = (ViewHolder) view.getTag(); // 重新获取ViewHolder
23 }
24 viewHolder.letterImage.setImageResource(letter.getImageId());
25 viewHolder.letterName.setText(letter.getName());
26 return view;
27 }
28
29 class ViewHolder {
30 ImageView letterImage;
31 TextView letterName;
32 }
33 }
我们新增了一个内部类 ViewHolder,用于对控件的实例进行缓存。当 convertView为空的时候, 创建一个 ViewHolder 对象, 并将控件的实例都存放在 ViewHolder里, 然后调用 View的 setTag()方法,将 ViewHolder 对象存储在 View 中。当 convertView 不为空的时候则调用View的 getTag()方法, 把 ViewHolder重新取出。 这样所有控件的实例都缓存在了 ViewHolder里,就没有必要每次都用findViewById()方法来获取控件实例了。
二、ListView的点击事件:
为ListView添加点击事件只需在主活动中为ListView注册监听器即可:
1 @Override
2 protected void onCreate(Bundle savedInstanceState) {
3 super.onCreate(savedInstanceState);
4 setContentView(R.layout.activity_main);
5 initLetters(); // 初始化数据
6 LetterAdapter adapter = new LetterAdapter(MainActivity.this,
7 R.layout.letter_item, letterList);
8 ListView listView = (ListView) findViewById(R.id.list_view);
9 listView.setAdapter(adapter);
10 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
11 @Override
12 public void onItemClick(AdapterView<?> parent, View view,
13 int position, long id) {
14 Letter letter = letterList.get(position);
15 Toast.makeText(MainActivity.this, letter.getName(),
16 Toast.LENGTH_SHORT).show();
17 }
18 });
19 }
可以看到,我们使用了 setOnItemClickListener()方法来为 ListView 注册了一个监听器,当用户点击了 ListView 中的任何一个子项时就会回调 onItemClick()方法, 在这个方法中可以通过 position 参数判断出用户点击的是哪一个子项,然后获取到相应的水果,并通过 Toast将字母显示出来。
//End.