首先这个控件为什么是最常用的呢?因为手机屏幕大小有限,有些东西比如新闻、手机联系人微博等大量数据不可能全部显示出来,需要通过滑动手机屏幕翻阅信息。这种方法用到的控件就是ListView
1、ListView的简单用法
首先创建一个工程名为ListViewTest,然后在activity_main.xml中添加如下代码:
然后在MainActivity.java中添加:
其中adapter的作用是设置ListView的布局和数据,第一个参数是context,第二个参数是Android自带的布局,第三个参数是数据。如果是微博,那么这个数据就来自网络,通讯录就来自本地。
效果如下:
2、定制ListView的界面
之前的界面太单调了,下面介绍怎么显示图片+文字。
首先创建一个水果类,命名为Fruit:
package com.example.listviewtest;
public class Fruit {
private String name; // 水果名
private int imageId; // 水果图片id
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
然后创建显示水果类的布局fruit_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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageView"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/textView"
android:layout_marginLeft="10dp"/>
</LinearLayout>
注意一些宽度和距离的设置。
由于这个布局中显示的内容包括图片和文字,所以需要自定义适配器来显示:
public class FruitAdapter extends ArrayAdapter<Fruit>{
private int resourceId;
public FruitAdapter(Context context, int resource, List<Fruit> objects) {
super(context, resource, objects);
resourceId = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit item = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId, null);
TextView tv = (TextView) view.findViewById(R.id.textView);
ImageView iv = (ImageView) view.findViewById(R.id.imageView);
tv.setText(item.getName());
iv.setImageResource(item.getImageId());
return view;
}
}
其中getView函数在每一个子项被滚动到屏幕中显示的时候会被调用。在getView 方法中,首先通过getItem()方法得到当前项的Fruit 实例,然后使用LayoutInflater 来为这个子项加载我们传入的布局,接着调用View 的findViewById()方法分别获取到ImageView 和TextView 的实例,并分别调用它们的setImageResource()和setText()方法来设置显示的图片和文字,最后将布局返回,这样我们自定义的适配器就完成了。
最后修改MainActivity的代码:
这样水果的数据和界面都加载完成了。效果如下:
3、提升ListView运行效率
之前说到ListView难用,原因之一就是有很多地方可以优化,其中一个地方就是修改getView函数:getView每次都会把布局重新加载一遍,所以当ListView快速滚动的时候,性能会产生瓶颈。
getView中还有一个convertView参数,它就是之前加载的布局的缓存,所以可以这样得到view:
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
}
else view = convertView;
这样在快速滚动ListView的时候效率会有所提升。但是还可以优化,因为每次还是要通过findViewById才能获取控件的实例,所以可以用一个ViewHolder来保存控件,如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Fruit item = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) view.findViewById(R.id.imageView);
viewHolder.textView = (TextView) view.findViewById(R.id.textView);
view.setTag(viewHolder); // 将viewHolder存储到View中
}
else {
view = convertView;
viewHolder = (ViewHolder)view.getTag(); // 重新获取ViewHolder
}
viewHolder.textView.setText(item.getName());
viewHolder.imageView.setImageResource(item.getImageId());
return view;
}
当view是新加载的时候,会把TextView和ImageView赋值给ViewHolder,并且附到View的TAG中,当读取缓存的时候再从View中获取回来。
4、ListView的点击事件
如果ListView没有点击事件,那也是没什么作用的,所以需要设置每一项的点击事件。注意不是OnClickListener,而是OnItemClickListener。
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
下面说说这四个参数是什么意思(来自20110921 onItemClick监听器四个arg参数):首先AdapterView表示你现在点击的ListView是哪个,通过View可以获取ListView里面某一项的内容(比如可以获取某一个水果的TextView或者ImageView),position表示点击的item在适配器里的位置,id表示点击的item是listview的第几行,在大部分情况下id和position是相同的。
下面验证通过position获取的水果名和通过view获取的是一样的:
TextView fruitText = (TextView) view.findViewById(R.id.textView);
Toast.makeText(MainActivity.this, fruitText.getText().toString()+" "+fruit.getName(), Toast.LENGTH_SHORT).show(); // 输出两者相同
下面是自己另外加的内容:设置长按删除点击事件。长按删除包括在list中删除对应水果和更新适配器。
listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long id) {
fruitList.remove(position); // 删除对应水果
adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList); // 更新适配器
listview.setAdapter(adapter);
return true;
}
});