Android 控件之 ListView

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) {
        ......
    }
});

你可能感兴趣的:(Android 控件之 ListView)