首页 > 代码库 > 第二章 吸引你的眼球—UI编程(5)

第二章 吸引你的眼球—UI编程(5)

2.1.7列表组件(ListView)

ListView在Android中也是一个使用比较频繁的组件。它相对于其他的基本组件来说,使用起来稍微复杂一些,需要注意的也比较多,尤其是和其他一些组件组合起来使用的情况。

在Android中,ListView用来显示一个列表的组件,它以列表的形式展示具体的内容,并且能够根据数据的长度自适应显示。用户可以选择并操作这个列表,同时会触发相应的事件:当鼠标滚动时会触发setOnItemSelectedListener事件;当点击列表时会触发setOnItemClickListener事件。

列表的显示需要三个元素:

1) ListView:用来展示列表的View;

2) 适配器:用来把数据映射到ListView上的中介;

3) 数据:具体的将被映射的字符串、图片或者其他基本组件。

根据列表的适配器类型,列表分为三种:ArrayAdapter、SimpleAdapter和SimpleCursorAdapter。其中以ArrayAdapter最为简单,只能展示一行字;SimpleAdapter有最好的扩充性,可以自定义出各种效果;SimpleCursorAdapter可以认为是SimpleAdapter对数据库的简单结合,可以方便地把数据库中的内容以列表的形式展示出来。

下面,我们分别以一个例子来看看这些ListView是如何实现的。

1)首先,我们来看看ArrayAdapter,部分代码如下:

private ListView listView;

 

    @Override

    public void onCreate(Bundle savedInstanceState){

        super.onCreate(savedInstanceState);

        listView = new ListView(this);

        listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1,getData()));

        setContentView(listView);

    }

    

    private List<String> getData(){

        List<String> data = http://www.mamicode.com/new ArrayList();

        data.add("第一行");

        data.add("第二行");

        data.add("第三行");

        return data;

    }

 

让我们来看看效果,如图2-15所示:

技术分享

图2-15 ArrayAdapter的使用

 

这种列表使用了ArrayAdapter(Context context,int textViewResourceId, List<T>objects)来装配数据,ArrayAdapter的构造需要三个参数,依次为Context、布局文件(注意这里的布局文件描述的是列表的每一行的布局,android.R.layout.simple_list_item_1是系统定义好的布局文件只显示一行文字,数据源(一个List集合)。同时用setAdapter()完成适配。

2)SimpleCursorAdapter是把从游标得到的数据进行列表显示,并可以把指定的列映射到对应的TextView中。

下面的例子从电话簿中把联系人显示到类表中。先在通讯录中添加一个联系人作为数据库的数据。然后获得一个指向数据库的Cursor并且定义一个布局文件(当然也可以使用系统自带的)。

private ListView listView;

    

    @Override

    public void onCreate(Bundle savedInstanceState){

        super.onCreate(savedInstanceState);

        listView = new ListView(this);

        Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);

        startManagingCursor(cursor);

        ListAdapter listAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_expandable_list_item_1,

                cursor,

                new String[]{People.NAME},

                new int[]{android.R.id.name});

        listView.setAdapter(listAdapter);

        setContentView(listView);

    }

 

SimpleCursorAdapter 构造函数前面3个参数和ArrayAdapter是一样的,最后两个参数:一个包含数据库的列的String型数组,一个包含布局文件中对应组件id的int型数组。其作用是自动的将String型数组所表示的每一列数据映射到布局文件对应id的组件上。上面的代码,将NAME列的数据一次映射到布局文件的id为name的组件上。

另外,读取通讯录需要在AndroidManifest.xml中如权限:<uses-permissionandroid:name="android.permission.READ_CONTACTS"></uses-permission>。

效果如图2-16所示

技术分享

图2-16 SimpleCursorAdapter的使用

 

3)SimpleAdapter是我们比较常用的一种列表。它的扩展性最好,可以定义各种各样的布局出来,可以放上ImageView(图片),还可以放上Button(按钮),CheckBox(复选框)等等。我们也以一个例子来看看它是如何实现的。

首先,我们定义一个list_item.xml文件来定义list的内容:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

    xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="horizontal"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    <TextView

        android:id="@+id/name_textview"

        android:layout_height="wrap_content"

        android:layout_width="100dp"/>

    <TextView

        android:id="@+id/phone_textview"

        android:layout_height="wrap_content"

        android:layout_width="150dp"/>

    <Button

        android:id="@+id/call_button"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:text="呼叫"/>

</LinearLayout>

 
在这里,我们定义两个TextView、一个Button按钮,接着我们新建一个MyList类继承自ListActivity(ListActivity类继承Activity类,默认绑定了一个ListView(列表视图)界面组件,并提供一些与列表视图、处理相关的操作),部分代码如下:

@Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        SimpleAdapter adapter = new SimpleAdapter(this,getData(),R.layout.list_item,

                new String[]{"name","phone"},

                new int[]{R.id.name_textview,R.id.phone_textview});

        setListAdapter(adapter);

    }

 

    private List<Map<String, Object>> getData() {

        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

        Map<String, Object> map = new HashMap<String, Object>();

        map.put("name", "张三");

        map.put("phone", "13472345623");

        list.add(map);

 

        map = new HashMap<String, Object>();

        map.put("name", "李四");

        map.put("phone", "13472345623");

        list.add(map);

        map = new HashMap<String, Object>();

        map.put("name", "王五");

        map.put("phone", "13472345623");

        list.add(map);

        return list;

    }
 
下面,我们来看看效果,如图2-17所示:
 技术分享

图2-17 SimpleAdapter的使用

 
当我们点击按钮时,需要进行一系列的操作,但是上面那样做无法满足我们的需求。这是因为按钮是无法映射的,即使我们成功的用布局文件显示出了按钮也无法添加按钮的响应,这样的话就需要我们做一些别的事情来监听按钮事件:
首先,需要定义一个类ViewHolder,它里面定义列表中的三个组件: 

public final class ViewHolder{

        public TextView name;

        public TextView phone;

        public Button call;

    }

 
然后,我们定义一个mData用于存储数据:
privateList<Map<String, Object>> mData;
 
接着,我们定义一个类MyAdapter继承自BaseAdapter:

public class MyAdapter extends BaseAdapter{

 

        private LayoutInflater mInflater;

 

        public MyAdapter(Context context){

            this.mInflater = LayoutInflater.from(context);

        }

        @Override

        public int getCount() {

            return mData.size();

        }

        @Override

        public Object getItem(int arg0) {

            return null;

        }

        @Override

        public long getItemId(int arg0) {

            return 0;

        }

        @Override

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

            ViewHolder holder = null;

            if (convertView == null) {

                holder=new ViewHolder(); 

                convertView = mInflater.inflate(R.layout.list_item, null);

                holder.name = (TextView)convertView.findViewById(R.id.name_textview);

                holder.phone = (TextView)convertView.findViewById(R.id.phone_textview);

                holder.call = (Button)convertView.findViewById(R.id.call_button);

                convertView.setTag(holder);

            }else {

                holder = (ViewHolder)convertView.getTag();

            }

holder.name.setText((String)mData.get(position).get("name"));

holder.phone.setText((String)mData.get(position).get("phone"));

holder.call.setOnClickListener(new View.OnClickListener() {

                @Override

                public void onClick(View v) {

                Log.d(“test”,“call the number”);

                }

            });

            return convertView;

        }

    }

 
最后是onCreate方法:

@Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        mData = http://www.mamicode.com/getData();

        MyAdapter adapter = new MyAdapter(this);

        setListAdapter(adapter);

    }

 
这样的话,我们就可以对列表中的每一个组件进行事件监听了。如果我们要对ListView的每一行的点击事件进行监听的话,我们需要实现OnItemClickListener接口中的onItemClick方法:

@Override

public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {

    Log.d("test", "item: "+ arg2);

}
 
上面介绍的是使用ListActivity方式,因为它默认绑定了一个ListView,所以我们并不需要自己定义ListView就能直接使用了,但是很多时候,我们可能是在布局中自己定义一个ListView,这样我们可以自己来控制它的大小等属性,例如:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

    xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    <ListView

        android:id="@android:id/list"

        android:layout_width="fill_parent"

        android:layout_height="fill_parent"/>

</LinearLayout>
 
这里需要注意的是,这个ListView的id必须为"@android:id/list",然后我们通过以下的代码来实现:

AdapterView<?> mAdapterView = (ListView) findViewById(android.R.id.list);

((AbsListView) mAdapterView).setAdapter(adapter);

 
如果用这种方法的话,我们除了可以实现OnItemClickListener接口之外,也可以直接对ListView绑定监听事件来达到处理点击列表每一行的效果:

mAdapterView.setOnItemClickListener(new OnItemClickListener() {

    @Override

   public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {

         Log.d("test", "item: "+arg2);

}

});

 

经验分享:   

有的时候,我们为列表添加了OnItemClickListener监听,但是点击每一行后并没有效果。这是因为,如果列表中有CheckBox或者是Button等存在的话,他们默认是获得焦点的,而ListView的item能被选中的条件是要获得焦点,因为我们在根控件下设置如下属性:

android:descendantFocusability="blocksDescendants",这样Item Layout就屏蔽了所有子控件获取Focus的权限,而自己能够获得焦点响应点击事件了。

第二章 吸引你的眼球—UI编程(5)