ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚到屏幕内,同时屏幕上原有的数据则会滚到屏幕外。
1.ListView的简单用法
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_view">
</ListView>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
//定义一个字符数组,用来表示一会屏幕中要出现的内容
private String[] data={"Apple","banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango","Apple","banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//适配器模式把一个类的接口转换成客户端期待的另外一种接口, 从而是原本应用接口不匹配而无法在一起工作的两个类能够在一起工作.这里通过适配器将数组转化为listview能够使用的数据
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listView=(ListView)findViewById(R.id.list_view);
//将构建好的适配器对象传递进去
listView.setAdapter(adapter);
}
}
2.定制ListView的界面
首先定义一个Fruit类来作为ListView的适配器适配类型。可以这么理解适配器:由于ListView只能显示List类的东西,但我们想要显示Fruit类的对象,这时候就需要适配器,让Fruit类来适配List类,这样我们就可以在ListView中显示Fruit类的对象了。
class Fruit {
private String name;
private int imageId;
public Fruit(String name,int imageId){
this.imageId=imageId;
this.name=name;
}
public String getName(){
return name;
}
public int getImageId(){
return imageId;
}
}
然后需要为ListView的子项指定一个我们自定义的布局,在layout目录下新建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/fruit_image">
</ImageView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fruit_name"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp">
</TextView>
</LinearLayout>
(这样理解,ListView是要显示一行一行的东西的,每一行都会有一个相同的格式,所以我们需要来定义一个具体的格式,也就是这个xml文件)
接下来需要创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Fruit类。新建FruitAdapter类。
class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
super(context, resource, objects);
resourceId=resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
Fruit fruit=getItem(position);
View view= LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
ImageView fruitImage=(ImageView)view.findViewById(R.id.fruit_image);
TextView fruitName=(TextView)view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
}
这个适配器的继承类重写了父类的一组构造函数,用于将上下文,ListView子项布局的id和数据都传递进来。另外重写了getView方法,这个方法在每个子项被滚到屏幕内的时候会被调用。在getView方法中,首先铜鼓欧getItem方法得到当前项的Fruit实例,然后使用LayoutInflater来为这个子项加载我们传入的布局。
这里LayoutInflater的inflate方法接受3个参数,前两个参数我们已经知道了意思,第三个参数指定成false,表示只让我们在父布局中声明的layout属性生效,但不会为这个View添加父布局,因为一旦View有了父布局之后,他就不能再添加到Listview中了。
之后调用view的findViewById方法分别获取到ImageView和TextView的实例,并分别调用他们的setImageSource方法和Settext方法来显示图片和文字,最后将布局返回。
下面修改JAVA文件中的代码:
public class MainActivity extends AppCompatActivity {
//实例Fruit类类型的List对象
private List<Fruit> fruitList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化水果数据,这时候fruitList对象中已经存储好了各种水果和图片
initFruits();
//适配器模式把一个类的接口转换成客户端期待的另外一种接口, 从而是原本应用接口不匹配而无法在一起工作的两个类能够在一起工作.这里通过适配器将数组转化为listview能够使用的数据
FruitAdapter adapter=new FruitAdapter(
MainActivity.this,R.layout.fruit_item,fruitList
);
ListView listView=(ListView)findViewById(R.id.list_view);
//将构建好的适配器对象传递进去
listView.setAdapter(adapter);
}
private void initFruits(){
for(int i=0;i<2;i++){
Fruit apple = new Fruit("Apple",R.mipmap.plant1);
fruitList.add(apple);
Fruit banana = new Fruit("Banana",R.mipmap.plant3);
fruitList.add(banana);
Fruit orange = new Fruit("Orange",R.mipmap.plant4);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon",R.mipmap.plant3);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear",R.mipmap.plant3);
fruitList.add(pear);
Fruit grape = new Fruit("Grape",R.mipmap.plant3);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple",R.mipmap.plant3);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry",R.mipmap.plant3);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry",R.mipmap.plant3);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango",R.mipmap.plant3);
fruitList.add(mango);
}
}
}
只要修改fruit—item.xml中的内容,就可以定制出各种复杂的界面了!!!
3.提升ListView的运行效率
getView方法中还有一个converView参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。
class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
super(context, resource, objects);
resourceId=resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
Fruit fruit=getItem(position);
View view;
if(convertView==null)
view= LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
else
view=convertView;
ImageView fruitImage=(ImageView)view.findViewById(R.id.fruit_image);
TextView fruitName=(TextView)view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
}
这样就提高了效率,在快速滚动的时候也可以表现出更好的性能。
不过还是可以继续优化的,虽然不会在重复去加载布局,但是getView方法还是调用findViewById方法获取控件的实例。我们可以借助一个ViewHolder来对这部分性能进行优化。
class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
super(context, resource, objects);
resourceId=resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
Fruit fruit=getItem(position);
View view;
ViewHolder viewHolder;
if(convertView==null){
view= LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.imageView=(ImageView)view.findViewById(R.id.fruit_image);
viewHolder.textView=(TextView)view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);//将ViewHOlder存储在View中
}
else{
view=convertView;
viewHolder=(ViewHolder)view.getTag();//重新获取ViewHolder
}
viewHolder.imageView.setImageResource(fruit.getImageId());
viewHolder.textView.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView imageView;
TextView textView;
}
}