【Android】ListView与RecyclerView基础运用
ListView
简单用法
首先在activity_main中加入ListView控件。
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
而后修改MainActivity中的代码
java
public class MainActivity extends AppCompatActivity {
String[] data = {"apple","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1,data);
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) {
String fruit = data[position];
Toast.makeText(MainActivity.this,fruit,Toast.LENGTH_SHORT).show();
}
});
}
}
-
ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, data);
- 这行代码创建了一个
ArrayAdapter
对象,它是Android中用于将数据绑定到ListView
、GridView
等组件的适配器之一。 <String>
表示这个适配器是用来处理字符串(String)类型的数据。new ArrayAdapter<>(...
表示实例化一个ArrayAdapter
对象。MainActivity.this
是当前MainActivity
的上下文(Context),这通常用于获取系统服务或启动其他组件。android.R.layout.simple_list_item_1
是一个系统提供的布局资源,用于定义列表项的样式。这里指定了列表项的布局,该布局显示单个文本项。data
是一个字符串数组,它包含了要显示在列表中的所有数据。
- 这行代码创建了一个
-
ListView listView = (ListView) findViewById(R.id.list_view);
- 这行代码通过调用
findViewById
方法来获取布局文件中定义的ListView
组件的实例。 R.id.list_view
是ListView
组件在布局文件中的ID。ListView
是Android中的一个组件,用于显示垂直滚动的列表项。
- 这行代码通过调用
-
listView.setAdapter(adapter);
- 这行代码将之前创建的
adapter
设置为listView
的适配器。 setAdapter
是ListView
的一个方法,用于将适配器绑定到列表视图上,这样列表视图就可以显示适配器中的数据了。
- 这行代码将之前创建的
ListView界面定制
新建类Fruit,并建立布局
java
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;
}
}
而后为ListView的子项指定一个自定义的布局,在layout目录下新建一个fruit_item.xml
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">
<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>
创建自定义的适配器
接着我们需要创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型是定位Fruit类
java
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId,objects);
resourceId = textViewResourceId;
}
@NonNull
@Override
public 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;
}
}
public class FruitAdapter extends ArrayAdapter<Fruit> {
- 定义了一个名为
FruitAdapter
的公共类,它继承自ArrayAdapter
类,并且指定了泛型参数Fruit
,这意味着这个适配器是专门为处理Fruit
类型对象的列表而设计的。
- 定义了一个名为
private int resourceId;
- 声明了一个私有的整型变量
resourceId
,用于存储列表项的布局资源ID。
- 声明了一个私有的整型变量
public FruitAdapter(@NonNull Context context, int textViewResourceId, List<Fruit> objects) {
- 定义了
FruitAdapter
的构造函数,它接受三个参数:@NonNull Context context
:上下文对象,用于访问系统服务和资源。int textViewResourceId
:列表项的布局资源ID。List<Fruit> objects
:一个包含Fruit
对象的列表,这些对象将被显示在列表中。
- 定义了
super(context, textViewResourceId, objects);
- 调用父类
ArrayAdapter
的构造函数,传递上下文、资源ID和对象列表。
- 调用父类
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
- 重写了
ArrayAdapter
中的getView
方法,它是适配器的核心,用于为列表中的每个项提供视图。 int position
:当前项在列表中的位置。@Nullable View convertView
:可重用的视图,用于提高列表滚动的性能。@NonNull ViewGroup parent
:列表视图的父容器。
- 重写了
Fruit fruit = getItem(position);
- 调用
ArrayAdapter
的getItem
方法,获取列表中当前位置的Fruit
对象。
- 调用
View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
- 使用
LayoutInflater
从getContext()
获取的上下文中实例化一个视图,使用resourceId
指定的布局资源,并且parent
是列表视图的父容器,false
表示不要为parent
添加这个视图。
- 使用
LayoutInflater.from(getContext())
LayoutInflater
是一个用于将XML布局文件转换成实际视图对象的系统服务。from(context)
是一个静态方法,它返回一个LayoutInflater
的实例,并且已经配置了给定的上下文(context
)。getContext()
- 这是
ArrayAdapter
类的一个方法,返回当前适配器的上下文(Context
)。上下文是Android中非常重要的概念,它提供了对系统服务的访问以及对资源的引用。inflate(resourceId, parent, attachToRoot)
inflate
是LayoutInflater
类的一个方法,用于将指定资源ID的XML布局文件实例化为视图对象。resourceId
:是一个整型值,指定了要实例化的布局资源的ID。parent
:是一个ViewGroup
对象,表示新创建的视图将要被添加到的父容器。在这个上下文中,parent
参数通常是用来确保新视图的布局参数正确设置的。attachToRoot
:是一个布尔值,指定是否将新创建的视图附加到parent
。在这个例子中,false
表示不附加到parent
,而是返回一个独立的视图对象,稍后可以手动添加到parent
。综合来看,
LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
这行代码的作用是:
- 从当前适配器的上下文中获取
LayoutInflater
实例。- 使用
inflate
方法将指定的布局资源ID(resourceId
)转换成实际的视图对象。- 将这个新创建的视图作为子视图,但不立即附加到提供的父容器(
parent
)上。- 返回这个新创建的视图对象,以便可以在适配器的
getView
方法中进一步设置和返回。
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
- 通过
findViewById
获取布局中的ImageView
组件,用于显示水果的图片。
- 通过
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
- 通过
findViewById
获取布局中的TextView
组件,用于显示水果的名称。
- 通过
fruitImage.setImageResource(fruit.getImageId());
- 设置
ImageView
的图片资源,getImageId()
是Fruit
类的一个方法,返回图片资源的ID。
- 设置
fruitName.setText(fruit.getName());
- 设置
TextView
的文本,getName()
是Fruit
类的一个方法,返回水果的名称。
- 设置
return view;
- 返回为当前列表项创建的视图。
FruitAdapter
类通过重写getView
方法,为每个列表项提供了自定义的视图布局,包括水果的图片和名称。这使得列表项不仅显示文本,还可以显示图像,从而提供更丰富的用户界面。
在MainActivity中调用
这一步骤只需要替换上一阶段中的适配器对象即可,此处略过。
ListView性能优化
将FruitAdapter中的代码进行优化。
java
public View getView(int position, @Nullable View convertView, @NonNull 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;
}
- 通过重用
convertView
,减少了布局的创建和视图的初始化次数。这对于长列表来说尤其重要,因为它可以显著减少内存使用和提高滚动性能。
java
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(@NonNull Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId,objects);
resourceId = textViewResourceId;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull 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.fruitImage = view.findViewById(R.id.fruit_image);
viewHolder.fruitName = view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);
}else{
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}
}
白学警告
此控件早在远古时代的16年就被淘汰。
RecyclerView
加入RecyclerView组件
xml
<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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
由于该组件不是内置在系统SDK中,所以需要把完整的包路径写出来。
为RecyclerView准备一个适配器
java
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View itemView) {
super(itemView);
fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image);
fruitName = (TextView) itemView.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> mFruitList) {
this.mFruitList = mFruitList;
}
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,
parent,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
@Override
public int getItemCount() {
return mFruitList.size();
}
}
这段代码定义了一个名为FruitAdapter
的类,它扩展了RecyclerView.Adapter
,用于在RecyclerView
中显示水果列表。
- 构造函数 :
public FruitAdapter(List<Fruit> mFruitList)
- 初始化适配器,并接收一个包含
Fruit
对象的列表。
- ViewHolder****静态内部类 :
static class ViewHolder extends RecyclerView.ViewHolder
- 定义了一个静态内部类
ViewHolder
,用于缓存视图组件的引用,减少重复调用findViewById
。
- onCreateViewHolder :
@Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
- 创建并返回一个新的
ViewHolder
实例。这个方法负责为每个列表项提供视图。
- onBindViewHolder :
@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position)
- 将数据绑定到
ViewHolder
上。这个方法负责将每个列表项的数据填充到视图中。
- getItemCount :
@Override public int getItemCount()
- 返回列表中项目的数量。这是
RecyclerView
用来确定显示多少项的方法。
- 构造函数 :
- 接收一个
Fruit
对象的列表,并将其存储在适配器中。
- 接收一个
- ViewHolder :
- 在
ViewHolder
的构造函数中,通过调用itemView.findViewById
获取视图中的组件(如ImageView
和TextView
),并将它们存储在ViewHolder
实例中。
- 在
- onCreateViewHolder :
- 使用
LayoutInflater
从parent.getContext()
获取的上下文中加载布局资源(R.layout.fruit_item
),并将其实例化为视图。 - 创建一个新的
ViewHolder
实例,并将新创建的视图传递给ViewHolder
的构造函数。 - 返回这个新的
ViewHolder
实例。
- 使用
- onBindViewHolder :
- 根据
position
参数获取列表中对应位置的Fruit
对象。 - 使用
ViewHolder
中的组件引用(fruitImage
和fruitName
)来设置视图的图片资源和文本。
- 根据
- getItemCount :
- 返回适配器中
Fruit
对象列表的大小,即列表项的总数。
- 返回适配器中
修改MainActivity中的代码
java
private List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
initFruits();
-
这行代码调用了一个名为
initFruits
的方法,这个方法可能是用来初始化一个包含Fruit
对象的列表,这些对象将被用于填充RecyclerView
。RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
-
通过调用
findViewById
方法,获取布局文件中定义的RecyclerView
组件的实例,并将其转换为RecyclerView
类型。LinearLayoutManager layoutManager = new LinearLayoutManager(this);
-
创建了一个
LinearLayoutManager
对象,用于管理RecyclerView
中的项如何布局。this
关键字指的是当前的上下文(可能是一个Activity
或Fragment
)。recyclerView.setLayoutManager(layoutManager);
-
将创建的
LinearLayoutManager
设置为RecyclerView
的布局管理器,这样RecyclerView
就知道如何对其子项进行布局。FruitAdapter adapter = new FruitAdapter(fruitList);
-
创建了一个
FruitAdapter
对象,并将之前初始化的fruitList
(包含Fruit
对象的列表)传递给它。这个适配器将用于将数据绑定到RecyclerView
。recyclerView.setAdapter(adapter);
-
将创建的
FruitAdapter
设置为RecyclerView
的适配器。这样RecyclerView
就有了数据源,并且知道如何显示这些数据。
实现横向滚动和瀑布流布局,网格布局
RecyclerView
是 Android 中一个灵活的组件,用于展示大量数据集,它可以配合不同的布局管理器(LayoutManager
)来实现横向滚动、瀑布流布局和网格布局。以下是实现这些布局的基本步骤:
横向滚动布局
横向滚动通常使用 LinearLayoutManager
并设置其方向为横向。
java
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
瀑布流布局(StaggeredGridLayoutManager)
瀑布流布局是一种不规则的网格布局,可以实现不同高度和宽度的项。
- 创建
StaggeredGridLayoutManager
实例:
java
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
网格布局
网格布局使用 GridLayoutManager
来实现。
- 创建
GridLayoutManager
实例:
java
GridLayoutManager layoutManager = new GridLayoutManager(this, spanCount);
recyclerView.setLayoutManager(layoutManager);
- 设置
RecyclerView
的布局管理器:
java
recyclerView.setLayoutManager(layoutManager);
注册点击事件
java
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,
parent,false);
ViewHolder holder = new ViewHolder(view);
//注册文字监听事件
holder.fruitName.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(view.getContext(),"外层", Toast.LENGTH_SHORT).show();
}
});
//注册图片按钮监听事件
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(view.getContext(),"图片", Toast.LENGTH_SHORT).show();
}
});
return holder;
}