ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有数据将会滚动出屏幕。
1、ListView简单用法
如何将ListView将你要显示的大量内容关联起来呢?这是个很重要的问题。
1、首先我们必须先将数据提供好,因为你的目的是要用ListView来展示数据嘛。我们先用一个数组来保存数据。
2、那么数组中的数据怎么才能传递给ListView呢?我们通过适配器来完成,Android中有很多适配器的实现类,比较好用的就是ArrayAdapter。它可以通过泛型来指定更适配的数据类型,然后在构造函数中把要适配的数据传入。ArrayAdapter有多个构造函数的重载,可以选择合适的一个。
3、要调用ListView地setAdapter()方法,将构建好的适配器对象传递进去、这样ListView和数据之间地关联就建立好了。
详细步骤:
新建一个ListViewTest项目,让AS自动创建好默认活动。假如目前活动已经创建好。
修改activity_main.xml中的代码,(在布局中添加ListView控件),如下:
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"tools:context="com.example.listviewtest.MainActivity"> <ListViewandroid:id="@+id/list_view"android:layout_width="match_parent"android:layout_height="match_parent"></ListView></LinearLayout>
修改MainActivity中的代码,如下:
package com.example.listviewtest;import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;import org.w3c.dom.Text;import java.util.List;public class MainActivity extends AppCompatActivity {private String[] data = {"A1","A2","A3","A4","A5","A6","A7","A8","A9","A10","A11","A12","A13","A14","A15","A16","A17","A18","A19","A20","A21","A22","A23","A24"};@Overrideprotected 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,data);ListView listView = (ListView) findViewById(R.id.list_view);listView.setAdapter(adapter);}
}
ListView是用来展示大量数据的,我们应该先将数据提供好,这些数据可以是从网上下载的,也可以是从数据库中读取的。
我们只是使用一个data数组来测试。
不过,数组中的数据是无法直接传递给ListView的,我们需要借助适配器来完成。Android 中提供了很多适配器的实现类,比较好用的就是ArrayAdapter。它可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入。ArrayAdapter有多个构造函数的重载,我们选择比较适合的一种。这里由于我们提供的数据都是字符串,因此将ArrayAdapter的泛型指定为String,然后在ArrayAdapter的构造函数中依次传入当前的上下文,ListView子项布局的id,以及要适配的数据。
注意:这里我们使用了android.R.layout.simple_list_item_1作为ListView子项布局的id,这是一个Android内置的布局文件,里面只有一个TextView,可用于简单地显示一段文本。
最后,我们还需要调用ListView地setAdapter()方法,将构建好的适配器对象传递进去、这样ListView和数据之间地关联就建立好了。
运行程序如下:
2、定制ListView的界面
只能够显示一段文本的ListView实在是太单调了,我们对ListView的界面进行定制,让它可以显示更加丰富的内容。
我们可以让这些名称的旁边显示一个图样。
定义一个实体类Fruit,作为ListView适配器的适配类型,新建Fruit,代码如下:
package com.example.listviewtest;public class Fruit {private String name;private int imageId;public Fruit(String name, int imageId) {this.name = name;this.imageId = imageId;}public String getName() {return name;}public int getImageId() {return imageId;}}
Fruit类中只有两个字段,name表示水果的名字,imageId表示水果对图片的资源id。
ListView的子项指定为一个我们呢我自定义的布局,在layout目录下新建fruit_item.xml,代码如下:
<?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"><ImageViewandroid:id="@+id/fruit_image"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid:id="@+id/fruit_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="10dp" /></LinearLayout>
我们定义了一个ImageView用于显示水果的图片,又定义了一个TextView用于显示水果的名称,并让TextView在垂直方向上水平居中。
接下来,需要创建一个自定义的适配器,这个设配器继承自ArrayAdapter,并将泛型指定为Fruit。新建FruitAdapter,
代码如下:
package com.example.listviewtest;import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;import java.util.List;/*** Created by ZHJ on 2018/3/3.*/public class FruitAdapter extends ArrayAdapter<Fruit> {private int resourceId; public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){super(context,textViewResourceId,objects);resourceId = textViewResourceId;}@NonNull@Overridepublic View getView(int position, @Nullable View convertView, @NonNull 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;}
}
FruitAdapter重写了父类的一组构造函数,用于将上下文、ListView子项布局的id和数据都传递进来。另外又重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候被调用。
在getView()方法中,
1、首先通过getItem()方法得到当前项的Fruit实例,
2、然后使用LayoutInflater来为这个子项加载我们传入的布局。LayoutInflater的inflate()方法接收3个参数,第一个参数是要加载的布局文件的id,第二个参数是给加载好的布局再添加一个父布局,第三个参数,我们指定为false,表示只让我们在父布局中声明的layout属性生效,但不会为这个View添加父布局。
调用View的findViewById()方法分别获取ImageView和TextView的实例,并分别调用它们的setImageResource()和setText()方法来设置显示的图片和文字。
接着修改MainActivity中的代码,如下:
package com.example.listviewtest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;import java.util.ArrayList;
import java.util.List;public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<Fruit>();//新建对象实例
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); initFruits(); // 初始化水果数据FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);//传入上下文、子项布局的id、数据ListView listView = (ListView) findViewById(R.id.list_view);listView.setAdapter(adapter);listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {Fruit fruit = fruitList.get(position);Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();}});}private void initFruits() {for (int i = 0; i < 2; i++) {Fruit apple = new Fruit("Apple", R.drawable.apple_pic);fruitList.add(apple);Fruit banana = new Fruit("Banana", R.drawable.banana_pic);fruitList.add(banana);Fruit orange = new Fruit("Orange", R.drawable.orange_pic);fruitList.add(orange);Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);fruitList.add(watermelon);Fruit pear = new Fruit("Pear", R.drawable.pear_pic);fruitList.add(pear);Fruit grape = new Fruit("Grape", R.drawable.grape_pic);fruitList.add(grape);Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);fruitList.add(pineapple);Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);fruitList.add(strawberry);Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);fruitList.add(cherry);Fruit mango = new Fruit("Mango", R.drawable.mango_pic);fruitList.add(mango);
}}}
可以看到这里添加了一个initFruits()方法,用于初始化所有的水果数据。在Fruit类中的构造函数将水果的名字和对应的图片id传入,然后把创建好的对象添加到水果列表中。
另外使用了一个for循环将所有的水果数据添加了两遍,只添加一遍数据还不足以充满整个屏幕。接着在onCreate()方法中创建了FruiteAdapter对象,并将FruitAdapter作为适配器传递给ListView,这样ListView界面就完成了。
提升ListView的运行效率
之所以说ListView这个控件很难运用,因为它有很多细节可以优化,其中运行效率就是很重要的一点。目前,我们的运行效率是很低的,因为在FruitAdapter的getView()方法中,每次都将布局重新加载了一遍,当ListView快速滚动的时候,这就会成为性能的瓶颈。
仔细观察会发现,getView()方法中还有一个convertView参数,这个参数用于将之前加载好的布局进行缓存,一遍日后重用,修改FruitAdapter中的代码,如下:
package com.example.listviewtest;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;import java.util.List;public class FruitAdapter extends ArrayAdapter<Fruit> {private int resourceId;//用来计数public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {super(context, textViewResourceId, objects);resourceId = textViewResourceId;}//构造函数@Overridepublic View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); // 获取当前项的Fruit实例View view;ViewHolder viewHolder;if (convertView == null) {view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);viewHolder = new ViewHolder();viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);view.setTag(viewHolder); // 将ViewHolder存储在View中 } else {view = convertView;viewHolder = (ViewHolder) view.getTag(); // 重新获取ViewHolder}viewHolder.fruitImage.setImageResource(fruit.getImageId());viewHolder.fruitName.setText(fruit.getName());return view;} class ViewHolder {ImageView fruitImage;TextView fruitName;}}