Android控件之ListView

ListView是android最常用的控件之一,允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据将滚动出屏幕,例如可用于显示联系人信息,系统设置等。

ListView用法

有以下两种方式实现ListView:

  1. XML中配置ListView
  2. activity继承ListActivity

下面分别介绍。

XML中配置ListView

在activity_main.xml中添加如下代码:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:headerDividersEnabled="false"
        android:listSelector="#001166"
        android:divider="#ff00ff"
        android:dividerHeight="1dp"
        />
</RelativeLayout>

其中几个重要的属性:

  • android:listSelector
    设置选中时的颜色/图片
  • android:divider
    设置选项之间的颜色/图片
  • android:dividerHeight
    设置选项之间的高度
  • android:entries
    静态设置选项数据

修改好布局文件之后,我们来修改一下对应的activity代码:


/**ListView练习
 * @author sywyg
 * @time 2015/4/12
 */

public class MainActivity extends Activity implements AdapterView.OnItemClickListener{
    private ListView lv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView)findViewById(R.id.list_view);
        String[] cities = {"南京","北京","上海","广州","深圳","徐州","丰县"};
        /**
         * ArrayAdapter参数
         * 1. 当前上下文Context对象
         * 2. ListView子项布局的id
         * 3. 需要适配的数组
         *
         */
        /*ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,names);
        //设置选项为单选
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_single_choice,names);
        lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        //设置选项为多选
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_multiple_choice,cities);
        lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);*/
        //获取适配器
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,cities);
        //绑定适配器
        lv.setAdapter(adapter);
        //事件监听
        lv.setOnItemClickListener(this);
    }

    /**
     * @param adapterView 当前ListView
     * @param view 选中的视图可能是控件也可能是布局
     * @param position 选中的第几个
     * @param id 选中的id
     */
    @Override
    public void onItemClick(AdapterView<?> adapterView, View view,int position, long id){
        ListView listView = (ListView)adapterView;
        //判断是选中还是撤销选择
        boolean ischecked = listView.isItemChecked(position);
        listView.setSelection(position);
        if(ischecked){
            Toast.makeText(this, ((TextView) view).getText().toString(), Toast.LENGTH_SHORT).show();
        }
        else{
            Toast.makeText(this, "撤销选择", Toast.LENGTH_SHORT).show();
        }

    }
}

onCreate()中我们首先创建了适配器对象ArrayAdapter实例,其中的ListView子项布局的id参数为系统提供的布局文件,其实我们完全可以自己定义,这个后续介绍。接着调用ListView的setAdapter()方法,设置适配器对象。最后,我们也设置了ListView的点击事件(实现AdapterView.OnItemClickListener,并覆盖onItemClick())。

我们这里使用了ArrayAdapter适配器,下面我们使用SimpleAdapter适配器实现图文并茂。

SimpleAdapter

SimpleAdapter适配器需要一个额外的布局文件,来配置ListView,新建一个布局文件item.xml,代码如下:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</LinearLayout>

这里包含两个控件,对应图片和文本。

而对于主xml仍然只有ListView控件,这里不再给出。

SimpleAdapter实例化需要接收五个参数,分别为:

  1. Context
    ListView显示的上下文
  2. List对象
    存放所有要显示的ListView选项。每个元素对应一个Map对象,每个Map对象表示ListView中的一行,和第4个参数字符串数组中的元素前后对应。
  3. int
    Resource identifier of a view layout that defines the views for this list item.
    定义ListView每个选项的布局文件id,即上面新建的布局文件item.xml的id。
  4. String[]
    对应第2个参数中Map对象的键。
  5. int[]
    对应第3个参数中布局中的控件id。

下面给出了一个简单的代码:


// 1.添加ListView选项,每个选项为图片和文字
Map<String,Object> item1 = new HashMap<String, Object>();
item1.put("image",R.drawable.abc_ab_share_pack_mtrl_alpha);
item1.put("name","sywyg");
Map<String,Object> item2 = new HashMap<String, Object>();
item2.put("image",R.drawable.abc_btn_check_material);
item2.put("name","wyg");
//Map列表项添加到List中
List< Map<String,Object>> list = new ArrayList< Map<String,Object>>();
list.add(item1);
list.add(item2);

// 2.映射关系
String[] from = {"image","name"};
int[] to = {R.id.iv,R.id.tv};

// 3.设置适配器,参数上面有说明
SimpleAdapter simpleAdapter = new SimpleAdapter(this,list,R.layout.item,from,to);

通过上面的处理,就创建了一个SimpleAdapter对象,接着调用ListView的setAdapter()方法。最后,设置ListView的点击事件(和上面的方法一样)。

定制ListView界面

虽然可以通过SimpleAdapter实现图文并茂,但是想要实现更复杂的布局,我们需要实现自定义的适配器,我们可以继承ArrayAdapter,SimpleAdapter,BaseAdapter,这里我们选择继承BaseAdapter抽象类。

实现代码如下:


public class MyAdapter<T> extends BaseAdapter {
    private List<T> mObjects;
    private Context mContext;
    private int resource;
    public MyAdapter(Context context,int resource,List<T> Objects) {
        super();
        mContext = context;
        this.resource = resource;
        mObjects = Objects;
    }
    // 获取列表项的总数
    @Override
    public int getCount() {
        return mObjects.size();
    }
    //获取列表项
    @Override
    public Object getItem(int position) {
        return mObjects.get(position);
    }
    //获取列表项id
    @Override
    public long getItemId(int position) {
        return position;
    }

    /**
     *    返回子视图,在每个子项被滚动到屏幕内的时候会被调用
     *    @param position 选中的位置
     *    @param convertView 转换视图,用于将之前加载好的布局进行缓存
     *                       通过它来优化ListView,提高性能
     *    @param parent 父组件
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = null;
        ViewHolder viewHolder = null;
        if(convertView == null) {
            // 获取显示ListView子项的布局
            view = LayoutInflater.from(mContext).inflate(resource, null);
            // 获取子布局中的控件
            viewHolder.iv = (ImageView) view.findViewById(R.id.iv);
            viewHolder.tv = (TextView) view.findViewById(R.id.tv);
            // 将ViewHolder对象保存在tag中
            view.setTag(viewHolder);
        } else{
            // 避免在每次调用该方法时重新加载布局
            view = convertView;
            // 取出ViewHolder对象
            viewHolder = (ViewHolder)view.getTag();
        }
        // 获取position位置的选项
        City city = (City)mObjects.get(position);
        // 设置控件值
        viewHolder.iv.setImageResource(city.getImage());
        viewHolder.tv.setText(city.getName());
        return view;
    }

    /**
     * 缓存控件的实例,避免多次实例化控件
     */
    static class ViewHolder{
        ImageView iv;
        TextView tv;
    }
}

继承BaseAdapter抽象类需要实现以上4个方法,另外在构造器中将上下文,ListView子布局id和数据传递进来。下面我们来重点讲一下getView()方法。

getView()用于返回视图,在每个子项被滚动到屏幕内的时候会被调用(首次调用次数为屏幕能够显示子项的个数,当然ListView的宽高是确定的,否则还是会反复调用确定宽高),position值从0到屏幕能够显示的最大列表项数减1进行循环(屏幕上显示的最大个数应该是正好显示个数加1)。在其中,转换视图convertView用于将之前加载好的且目前没有显示在屏幕上的一个子布局进行缓存(当position到达最大值之后在向下滑动,就会缓存第0个),通过它来优化ListView,提高性能,然后通过一个内部类ViewHolder缓存布局控件,避免每次加载视图时调用findViewById()

创建好适配器类之后,我们同样通过刚才的方法在Activity处理ListView,只不过现在使用的适配器是我们自定义的适配器。代码不再给出。

activity继承ListActivity

我们也可以通过继承ListActivity实现ListView,这种方法不需要我们提供ListView视图,只需设置适配器,监听器也可以通过该类的方法,如下:


public class ListActivityTest extends ListActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //创建适配器
        String[] cities = {"南京","北京","上海","广州","深圳","徐州","丰县"};
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_2,cities);
        //获得该activity的ListView
        //ListView listView = getListView();
        //设置ListView适配器
        setListAdapter(adapter);
    }

    /**
     * ListView事件监听,或者通过继承AdapterView.OnItemClickListener
     * 各参数和AdapterView.OnItemClickListener的方法onItemClick对应的一样。
     * @param l
     * @param v
     * @param position
     * @param id
     */
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        TextView textView = (TextView)v;
        Toast.makeText(this,textView.getText().toString(),Toast.LENGTH_SHORT).show();
    }
}

这种不需要我们配置布局,而是利用ListActivity提供的布局,当然我们也可以提供一个,但是必须包含一个id为android:id="@android:id/list"的ListView控件。

优化

  1. 利用convertView提高性能。
  2. 在xml配置ListView控件是固定其宽和高,以免多次绘制view(宽高不确定的话,就会反复确定),造成多次调用getView()
  3. 通过内部类ViewHolder,用来标识view中一些控件,不用每次调用findViewById(),提高了性能。设置ViewHolder为static,只会在第一次加载时会耗费较长的时间,同时保证了内存中只有一个ViewHolder,节省了内存的开销。
  4. 其它。。。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值