ListView优化

提升 ListView 的运行效率

之所以说 ListView这个控件很难用,就是因为它有很多的细节可以优化,其中运行效率就是很重要的一点。目前我们ListView的运行效率是很低的,因为在 FruitAdapter的getView()方法中每次都将布局重新加载了一遍,当 ListView快速滚动的时候这就会成为性能的瓶颈。仔细观察,getView()方法中还有一个 convertView 参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。修改 FruitAdapter中的代码,如下所示:

public class FruitAdapter extends ArrayAdapter<Fruit> {
……
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    Fruit fruit = getItem(position);
    View view;
    if (convertView == null) {
      view = LayoutInflater.from(getContext()).inflate(resourceId, null);
    } else {
      view = convertView;
    }
    ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
    TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
    fruitImage.setImageResource(fruit.getImageId());
    fruitName.setText(fruit.getName());
    return view;
  }
}

可以看到,现在我们在 getView()方法中进行了判断,如果 convertView 为空,则使用LayoutInflater 去加载布局,如果不为空则直接对 convertView进行重用。这样就大大提高了ListView的运行效率,在快速滚动的时候也可以表现出更好的性能。


不过,目前我们的这份代码还是可以继续优化的,虽然现在已经不会再重复去加载布局,但是每次在getView()方法中还是会调用View的findViewById()方法来获取一次控件的实例。我们可以借助一个 ViewHolder来对这部分性能进行优化,修改 FruitAdapter 中的代码,如下所示:

public class FruitAdapter extends ArrayAdapter<Fruit> {
……
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    Fruit fruit = getItem(position);
    View view;
    ViewHolder viewHolder;
    if (convertView == null) {
      view = LayoutInflater.from(getContext()).inflate(resourceId, null);
      viewHolder = new ViewHolder();
      viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
      viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
      view.setTag(viewHolder); //  将ViewHolder 存储在View 中
    } else {
      view = convertView;
      viewHolder = (ViewHolder) view.getTag(); //  重新获取ViewHolder
    }
    viewHolder.fruitImage.setImageResource(fruit.getImageId());
    viewHolder.fruitName.setText(fruit.getName());
    return view;
  }
  class ViewHolder {
    ImageView fruitImage;
    TextView fruitName;
  }
}

我们新增了一个内部类 ViewHolder,用于对控件的实例进行缓存。当 convertView为空的时候,创建一个 ViewHolder对象,并将控件的实例都存放在 ViewHolder 里,然后调用 View的 setTag()方法,将 ViewHolder 对象存储在 View 中。当 convertView 不为空的时候则调用View的 getTag()方法,把 ViewHolder 重新取出。这样所有控件的实例都缓存在了 ViewHolder里,就没有必要每次都通过 findViewById()方法来获取控件实例了。通过这两步的优化之后,我们 ListView的运行效率就已经非常不错了。

ListView 的点击事件

话说回来,ListView 的滚动毕竟只是满足了我们视觉上的效果,可是如果 ListView 中的子项不能点击的话,这个控件就没有什么实际的用途了。因此,本小节中我们就来学习一下ListView如何才能响应用户的点击事件。修改 MainActivity 中的代码,如下所示:

public class MainActivity extends Activity {
  private List<Fruit> fruitList = new ArrayList<Fruit>();
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initFruits();
    FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item, fruitList);
    ListView listView = (ListView) findViewById(R.id.list_view);
    listView.setAdapter(adapter);
    listView.setOnItemClickListener(new OnItemClickListener() {
    @Override
      public void onItemClick(AdapterView<?> parent, View view,
      int position, long id) {
        Fruit fruit = fruitList.get(position);
        Toast.makeText(MainActivity.this, fruit.getName(),
        Toast.LENGTH_SHORT).show();
    }
  });
}
……
}

可以看到,我们使用了 setOnItemClickListener()方法来为 ListView 注册了一个监听器,当用户点击了 ListView中的任何一个子项时就会回调 onItemClick()方法,在这个方法中可以通过 position 参数判断出用户点击的是哪一个子项,然后获取到相应的水果,并通过 Toast将水果的名字显示出来。重新运行程序,并点击一下西瓜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值