Android最常用和最难用的控件--ListView
程序员文章站
2022-03-29 23:10:19
最常用和最难用的控件—ListViewListView的简单用法//在布局中加入ListView控件,高度和宽度设置为match_parent,让其占满整个空间
最常用和最难用的控件—ListView
ListView的简单用法
//在布局中加入ListView控件,高度和宽度设置为match_parent,让其占满整个空间
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
//ListView用于展示大量数据
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
//修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
//事先创建好一组数据,但数组中的数据是无法直接传递给ListView的,所以要借助适配器完成
private String[] data = {"Apple","banner","Orange","Watermelon","pear","grape","pineapple","strawberry","cherry","mango",
"Apple","banner","Orange","Watermelon","pear","grape","pineapple","strawberry","cherry","mango"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//ArrayAdapter可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入
//ArrayAdapter有多个构造函数的重载,可以根据实际情况选择使用
//由于提供的数据为字符串,所以这里的泛型指定为String,然后在ArrayAdapter的构造函数中一次传入当前上下文、ListView子项布局的id,以及要适配的数据
//simple_list_item_1作为ListView子项布局的id,这是哟个Android内置的布局文件,里面只有一个TextView,用于简单的显示一段文本
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listView = (ListView) findViewById(R.id.list_view);
//最后调用setAdapter()方法将构建好的适配器对象传递进去,这样ListView和数据之间的关联就建立成了
listView.setAdapter(adapter);
}
}
- 运行结果
定制ListView的界面
- 实例说明:准备好一组图片,分别对应好以上提供的每一种说过,让水果的名称旁边都有一个图样
//新建Friut类,作为ListView的适配类型
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;
}
}
//在layout目录下新建fruit_item.xml 为ListView的子项指定一个我们自定义的布局
<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="100dp"
android:layout_height="100dp"/>
//名称
<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>
//创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Friut类,新建类FriutAdapter
//FruitAdapter继承自ArrayAdapter,泛型为Fruit
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
//FruitAdapter重写了一组父类的构造函数,将上下文、ListView子项布局的id、数据都传递进来
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){
super(context,textViewResourceId,objects);
resourceId = textViewResourceId;
}
//重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候会被调用
@Override
public View getView(int position, View convertView,ViewGroup parent) {
//使用getItem()方法得到Fruit实例
Fruit fruit = getItem(position);
//使用LayoutInflater来为这个子项加载我们传入的布局
//LayoutInflater的inflate()方法接收3个参数,第三个参数false,表示只让我们在父布局中声明layout属性生效,但补位这个View加载父布局,因为一旦有了父布局之后,他就不能再添加到ListView中了
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
//得到ImageView实例
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
//得到TextView实例
TextView fruitNmae = (TextView) view.findViewById(R.id.fruit_name);
//使用setImageResource()方法显示图片
fruitImage.setImageResource(fruit.getImageId());
//setText()方法显示名称
fruitNmae.setText(fruit.getName());
//将布局返回
return view;
}
}
//修改MainActivity
package com.example.listviewtest;
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化所有的水果数据
initFruits();
//创建FruitAdapter对象,并将FruitAdapter作为适配器传递给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循环将数据加载了两遍
for (int i = 0;i<2;i++){
//在Fruit类的构造函数中将水果的名称和对应的图片传入,然后把创建好的对象添加到水果列表中
Fruit apple = new Fruit("Apple",R.drawable.apple);
fruitList.add(apple);
Fruit banner = new Fruit("Banner",R.drawable.banner);
fruitList.add(banner);
Fruit orange = new Fruit("Orange",R.drawable.orange);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon",R.drawable.watermelon);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear",R.drawable.pear);
fruitList.add(pear);
Fruit grape = new Fruit("Grape",R.drawable.grape);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple",R.drawable.pineapple);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry",R.drawable.strawberry);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry",R.drawable.cherry);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango",R.drawable.mango);
fruitList.add(mango);
}
}
}
- 运行结果
提升ListView的运行效率
-
以上例子虽然实现了功能,但是ListView的运行效率还是很低,因为在FruitAdapter的getView()方法中,每次都将布局重新加载了一遍,当ListView快速滚动的时候,这就会成为性能的瓶颈
-
解决布局重复加载问题
//修改FriutAdapter public class FruitAdapter extends ArrayAdapter<Fruit> { private int resourceId; public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){ super(context,textViewResourceId,objects); resourceId = textViewResourceId; } @Override //convertView这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用 public View getView(int position, View convertView,ViewGroup parent) { Fruit fruit = getItem(position); View view; //对convertView进行判断,如果convertView为null,就使用LayoutInflater加载布局 if(convertView == null){ view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false); }else{ //如果convertView不为null,就对convertView进行重用 view = convertView; viewHolder = (ViewHolder) view.getTag(); } ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image); TextView fruitNmae = (TextView) view.findViewById(R.id.fruit_name); fruitImage.setImageResource(fruit.getImageId()); fruitNmae.setText(fruit.getName()); return view; } }
-
上述操作通过判断convertView是否为null来控制是否加载布局,解决了布局重复加载的问题。但是每次在getView()方法中还是会调用View的findViewById()方法来获取一次控件的实例,我们可以借助ViewHolder来对这部分性能进行优化
//修改FriutAdapter public class FruitAdapter extends ArrayAdapter<Fruit> { private int resourceId; public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){ super(context,textViewResourceId,objects); resourceId = textViewResourceId; } @Override //convertView这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用 public View getView(int position, View convertView,ViewGroup parent) { Fruit fruit = getItem(position); View view; ViewHolder viewHolder; //对convertView进行判断,如果convertView为null,就使用LayoutInflater加载布局 if(convertView == null){ view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false); //convertView为null时创建ViewHolder对象 viewHolder = new ViewHolder(); //将控件的实例都存放在ViewHolder里 viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image); viewHolder.fruitNmae = (TextView) view.findViewById(R.id.fruit_name); //调用View的setTag()方法将ViewHolder对象存在View中 view.setTag(viewHolder); }else{ //如果convertView不为null,就对convertView进行重用 view = convertView; //convertView不为null时则调用View的getTag()ff ,把 ViewHolder重新取出,这样所有控件的实例就都缓存在了ViewHolder里,就不必每次都通过findViewById()方法来获取实例了 viewHolder = (ViewHolder) view.getTag(); } viewHolder.fruitImage.setImageResource(fruit.getImageId()); viewHolder.fruitNmae.setText(fruit.getName()); return view; } //新增内部类ViewHolder,用于对控件的实例进行缓存 class ViewHolder{ ImageView fruitImage; TextView fruitNmae; } }
ListView的点击事件
-
点击ListView的任何一个子项,弹出提示
//修改MainActivity package com.example.listviewtest; public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化所有的水果数据 initFruits(); //创建FruitAdapter对象,并将FruitAdapter作为适配器传递给ListView, FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList); ListView listView = (ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); //使用setOnItemClickListener()方法为ListView注册了一个件监听器, // 当点击ListView中的任何一个子项时,会回调OnItemClick()方法,这个方法通过position判断用户点击的是哪一项,然后会获得相应的数据,并弹出提示 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public 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循环将数据加载了两遍 for (int i = 0;i<2;i++){ //在Fruit类的构造函数中将水果的名称和对应的图片传入,然后把创建好的对象添加到水果列表中 Fruit apple = new Fruit("Apple",R.drawable.apple); fruitList.add(apple); Fruit banner = new Fruit("Banner",R.drawable.banner); fruitList.add(banner); Fruit orange = new Fruit("Orange",R.drawable.orange); fruitList.add(orange); Fruit watermelon = new Fruit("Watermelon",R.drawable.watermelon); fruitList.add(watermelon); Fruit pear = new Fruit("Pear",R.drawable.pear); fruitList.add(pear); Fruit grape = new Fruit("Grape",R.drawable.grape); fruitList.add(grape); Fruit pineapple = new Fruit("Pineapple",R.drawable.pineapple); fruitList.add(pineapple); Fruit strawberry = new Fruit("Strawberry",R.drawable.strawberry); fruitList.add(strawberry); Fruit cherry = new Fruit("Cherry",R.drawable.cherry); fruitList.add(cherry); Fruit mango = new Fruit("Mango",R.drawable.mango); fruitList.add(mango); } } }
- 效果图
本文地址:https://blog.csdn.net/weixin_43704586/article/details/112513698
推荐阅读
-
Android实现ListView控件的多选和全选功能实例
-
Android最常用和最难用的控件--ListView
-
android ListView和ProgressBar(进度条控件)的使用方法
-
[Android] Android最简单ScrollView和ListView滚动冲突解决方案
-
Android TV开发,最简单的方式实现焦点控件放大动画
-
Android自定义控件从零开始-第三篇 深入解析View的事件和常用方法
-
3年以上勿进!最简单的Android自定义ListView下拉刷新与上拉加载,代码直接拿去用~
-
安卓开发学习笔记(五):史上最简单且华丽地实现Android Stutio当中Webview控件https/http协议的方法
-
Android最简单的限制输入方法(只包含数字、字母和符号)
-
Android实现ListView控件的多选和全选功能实例