ListView基础知识
-
ListView的简单用法
-
在activity_main.xml中引入ListView控件
<?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"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
-
在MainActivity.java中使用一个字符串数组作为数据
private String[] data = {"Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango","Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango"};
-
由于数组中的数据无法直接传递给ListView,所以要用适配器来完成。使用ArrayAdapter,它通过泛型来指定要适配的数据类型,它的构造方法接收三个参数,依次是:当前上下文、ListView子项布局(每一个item的布局)id(例子里用的是系统自带的android.R.layout.simple_list_item_1)、要适配的数据
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化一个适配器对象 ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, data); ListView listView = findViewById(R.id.list_view); //将构建好的适配器传递进去 listView.setAdapter(adapter); }
-
-
定制ListView的界面
-
在drawable准备好图片,接着定义实体类Fruit
public class Fruit { //水果名称 private String name; //对应的图片资源id 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; } }
-
为ListView的子项(每一个item)指定一个我们自定义的布局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="wrap_content"> <ImageView android:id="@+id/fruit_image" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android: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用于显示水果名称。
-
创建一个自定义适配器,让其继承自ArrayAdapter,并将泛型指定为Fruit类
public class FruitAdapter extends ArrayAdapter<Fruit> { private int resourceId; //构造器,用于将上下文,ListView子项布局id和数据传递进来 public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) { super(context, resource, objects); resourceId = resource; } /** * 这个方法在每个子项被滚动到屏幕内的时候被调用 * @param position * @param convertView * @param parent * @return */ @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { //得到当前项的Fruit实例 Fruit fruit = getItem(position); //inflate方法第一个参数是每一个item的布局文件id,第二个参数是添加的一个父布局 //第三个参数指定为false,表示只让我们在父布局中声明的layout属性生效,但是不为这个View添加父布局 View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); //通过view获取ImageView和TextView的实例,并设置显示的图片和文字 ImageView fruitImage = view.findViewById(R.id.fruit_image); TextView fruitName = view.findViewById(R.id.fruit_name); fruitImage.setImageResource(fruit.getImageId()); fruitName.setText(fruit.getName()); //将布局返回 return view; } }
-
修改MainActivity.java中的代码
private List<Fruit> fruitList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits(); FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList); 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.drawable.apple_pic); for (int j = 0; j < 30; j++) { fruitList.add(apple); } } }
activity_main.xml的代码不用修改,还是一个LinearLayout里面引入一个ListView。
-
-
提升ListView的运行效率
目前我们的ListView的效率是很低的,因为在FruitAdapter方法中的getView()方法,每次都将布局重新加载了一遍。
改进:getView()方法中有一个convertView参数,这个参数用于将之前加载好的布局进行缓存,便于重用。
修改FruitAdapter中的代码,通过下面的代码获取view:
View view; if (convertView == null) { //inflate方法第一个参数是每一个item的布局文件id,第二个参数是添加的一个父布局 //第三个参数指定为false,表示只让我们在父布局中声明的layout属性生效,但是不为这个View添加父布局 view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); } else { view = convertView; }
现在已经不会重复去加载布局,但是在getView()方法中还是会调用View的findViewById()方法获取一次控件的实例。
改进:借助ViewHolder进行优化
修改FruitAdapter中的代码:
/** * 这个方法在每个子项被滚动到屏幕内的时候被调用 * @param position * @param convertView * @param parent * @return */ @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { //得到当前项的Fruit实例 Fruit fruit = getItem(position); View view; ViewHolder holder; if (convertView == null) { //inflate方法第一个参数是每一个item的布局文件id,第二个参数是添加的一个父布局 //第三个参数指定为false,表示只让我们在父布局中声明的layout属性生效,但是不为这个View添加父布局 view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); holder = new ViewHolder(); //通过view获取ImageView和TextView的实例,并设置显示的图片和文字 holder.fruitImage = view.findViewById(R.id.fruit_image); holder.fruitName = view.findViewById(R.id.list_view); //将ViewHolder储存在view中 view.setTag(holder); } else { view = convertView; holder = (ViewHolder)view.getTag(); } holder.fruitImage.setImageResource(fruit.getImageId()); holder.fruitName.setText(fruit.getName()); return view; } class ViewHolder { ImageView fruitImage; TextView fruitName; }
新增了一个内部类ViewHolder,用于对控件的实例进行缓存。当convertView为null时,创建一个ViewHolder对象,并将控件的实例放在ViewHolder对象中,然后调用View的setTag()方法将ViewHolder对象储存在View中,当convertView不为空时,则调用View的getTag()方法取出ViewHolder对象。
-
ListView的点击事件
使用setOnItemClickListener()方法为ListView注册监听器
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Fruit fruit = fruitList.get(i); Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show(); } });
点击任何一个子项时,会回调onItemClick()方法,此方法的第三个参数i表示用户点击的是哪一个子项。
本文地址:https://blog.csdn.net/weixin_45922212/article/details/112575848
推荐阅读