首页 > 代码库 > 第二章 吸引你的眼球—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用于存储数据:
|
接着,我们定义一个类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)