Android控件之ListView
Android控件-----ListView
1.ListView的用法
ListView是安卓最常用和最难用的控件之一,它允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。比如查看QQ聊天记录,阅读新闻等等。
1.1.ListView的简单用法
ListView是用于展示大量数据的,这些数据可以直接定义在数组中,可以是从网上下载的,也可以是从数据库读取的。但是,数组中的数据不能直接传递给ListView,需要借助适配器来完成。在这里我们使用ArrayAdapter,它可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入。
(1)首先建立一个ListViewTest项目,选择Empty Activity选项。
在布局activity_main.xml中加入ListView控件,指定id,设置宽度和高度,这里都设置为match_parent。
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
(2)在MainActivity中定义数组,构建适配器。
指定适配器ArrayAdapter的泛型为,在其构造函数中传入当前上下文,ListView子项布局的id,以及要适配的数据。注意,这里我们使用的是android.R.layout.simple_list_item_1作为ListView子项布局的id,这是Android内置的布局文件,里面只有一个TextView,用于简单的显示一段文本。最后调用ListView的setAdapter()方法,将构造好的适配器对象传递进去,建立ListView和数据之间的关联。
private String[] arr = {"鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1,arr);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
1.2.自定义ListView
(1)定义类作为ListView适配器的适配类型
新建类中定义了两个字段,name表示十二生肖的名字,imageId表示十二生肖对应图片的资源id。
public class Zodiac {
private String name;
private int imageId;
public Zodiac(String name,int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
(2)重写子布局
在layout目录下新建zodiac_item.xml。
<?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">
<ImageView
android:id="@+id/zodiac_image"
android:layout_width="150dp"
android:layout_height="150dp"/>
<TextView
android:id="@+id/zodiac_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"/>
</LinearLayout>
(3)创建自定义适配器
自定义的适配器继承ArrayAdapter,并将泛型指定为Zodiac。重写了父类的构造函数,用于将上下文,ListView子项布局的id和数据传递进来,同时记录子项布局的id。还重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候被调用。首先通过getItem()方法得到当前项的Zodiac实例,然后使用LayoutInflater来为这个子项加载我们传入的布局。getContext()方法是得到当前布局的环境。
inflate(resourceId, parent, false)这里inflate方法接收三个参数,子项布局的id,父布局,(attachToRoot)false(表示只让我们在父布局中声明的layout属性生效,但不会为这个View添加父布局,因为一旦View有了父布局之后,它就不能再添加到ListView中了(暂且记住,等到View理解深刻就OK了))。
public class ZodiacAdapter extends ArrayAdapter<Zodiac> {
private int resourceId;
public ZodiacAdapter(Context context, int textViewResourceId, List<Zodiac> objects) {
super(context,textViewResourceId,objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Zodiac zodiac = getItem(position);//获取当前项的Zodiac实例
View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
ImageView zodiacImage = (ImageView) view.findViewById(R.id.zodiac_image);
TextView zodiacName = (TextView) view.findViewById(R.id.zodiac_name);
zodiacImage.setImageResource(zodiac.getImageId());
zodiacName.setText(zodiac.getName());
return view;
}
}
(4)在主活动中创建对象,将适配器传递给ListView
为了让他更丰富,我们准备一些图片。在res目录下新建一个drawable-xhdpi目录,将准备好的图片复制在该目录下。
public class MainActivity extends AppCompatActivity {
private List<Zodiac> zodiacList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initZodiac();
ZodiacAdapter adapter = new ZodiacAdapter(MainActivity.this,R.layout.zodiac_item,zodiacList);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
private void initZodiac() {
Zodiac rat = new Zodiac("鼠",R.drawable.rat);
zodiacList.add(rat);
Zodiac cow = new Zodiac("牛",R.drawable.cow);
zodiacList.add(cow);
Zodiac tiger = new Zodiac("虎",R.drawable.tiger);
zodiacList.add(tiger);
Zodiac rabbit = new Zodiac("兔",R.drawable.rabbit);
zodiacList.add(rabbit);
Zodiac dragon = new Zodiac("龙",R.drawable.dragon);
zodiacList.add(dragon);
Zodiac snake = new Zodiac("蛇",R.drawable.snake);
zodiacList.add(snake);
Zodiac horse = new Zodiac("马",R.drawable.horse);
zodiacList.add(horse);
Zodiac sheep = new Zodiac("羊",R.drawable.sheep);
zodiacList.add(sheep);
Zodiac monkey = new Zodiac("猴",R.drawable.monkey);
zodiacList.add(monkey);
Zodiac chicken = new Zodiac("鸡",R.drawable.chicken);
zodiacList.add(chicken);
Zodiac dog = new Zodiac("狗",R.drawable.dog);
zodiacList.add(dog);
Zodiac pig = new Zodiac("猪",R.drawable.pig);
zodiacList.add(pig);
}
}
2.提升ListView的运行效率
(1)目前ListView的运行效率很低,因为在ZodiacAdapter 的getView()方法中,每个子项被滚动到屏幕内的时候就会被调用,即每次都会将布局重新加载一遍,当ListView快速滚动时,这就会成为性能的瓶颈。因此我们使用getView()方法中的另一个参数convertView,这个参数用于将之前加载好的布局进行缓冲,以便于之后可以进行重用。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Zodiac zodiac = getItem(position);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
} else{
view = convertView;
}
ImageView zodiacImage = (ImageView) view.findViewById(R.id.zodiac_image);
TextView zodiacName = (TextView) view.findViewById(R.id.zodiac_name);
zodiacImage.setImageResource(zodiac.getImageId());
zodiacName.setText(zodiac.getName());
return view;
}
(2)每次使用getView()方法时,都会调用View的findViewById()方法来获取控件的实例。所以可以借助ViewHolder来对这部分性能进行优化。新增一个内部类ViewHolder,对控件的实例进行缓存。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Zodiac zodiac = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
viewHolder = new ViewHolder();
viewHolder.zodiacImage = (ImageView) view.findViewById(R.id.zodiac_image);
viewHolder.zodiacName = (TextView) view.findViewById(R.id.zodiac_name);
view.setTag(viewHolder); //将ViewHolder存储在View中
} else{
view = convertView;
//重新获取ViewHolder
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.zodiacImage.setImageResource(zodiac.getImageId());
viewHolder.zodiacName.setText(zodiac.getName());
return view;
}
class ViewHolder {
ImageView zodiacImage;
TextView zodiacName;
}
3.ListView的点击事件
使用setOnItemClickListener()方法为ListView注册一个监听器,当用户点击了ListView中的任何一个子项时,就会回调onItemClick()方法。在这个方法中可以通过position参数判断出用户点击的是哪一个子项,然后获取到相应的动物,并通过Toast将动物的名字显示出来。
public class MainActivity extends AppCompatActivity {
private List<Zodiac> zodiacList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initZodiac();
ZodiacAdapter adapter = new ZodiacAdapter(MainActivity.this,R.layout.zodiac_item,zodiacList);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Zodiac zodiac = zodiacList.get(position);
Toast.makeText(MainActivity.this,zodiac.getName(),Toast.LENGTH_SHORT).show();
}
});
}
...
}
点击猴子项: