Android RecyclerView 的基本使用方法和常用功能

好的,我们来系统地学习 Android RecyclerView 的基本使用方法和常用功能。RecyclerViewListViewGridView 的强大替代品,提供了更高的灵活性和性能。

一、基本使用步骤

使用 RecyclerView 需要五个核心组成部分,像一个团队协作:

  1. RecyclerView 自身:在布局中定义的视图容器。
  2. 数据源:一个包含所有要显示数据的列表(如 List<YourItem>)。
  3. ViewHolder:描述单个列表项视图的类,用于缓存视图,提高性能。
  4. Adapter:桥梁,将数据绑定到 ViewHolder 上。
  5. LayoutManager:负责 RecyclerView 的布局排列方式(线性、网格、瀑布流)。

步骤 1:添加依赖

app/build.gradle 文件中添加依赖。

dependencies {
    implementation "androidx.recyclerview:recyclerview:1.3.2"
}

步骤 2:在布局文件中添加 RecyclerView

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

步骤 3:创建列表项布局

创建单个列表项的布局文件 item_user.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"
    android:orientation="horizontal"
    android:padding="16dp">

    <ImageView
        android:id="@+id/ivAvatar"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tvName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginStart="16dp"
        android:text="Name"
        android:textSize="18sp" />

</LinearLayout>

步骤 4:创建数据模型 (Item Class)

创建一个数据类来表示列表中的每一项。

Kotlin:

data class User(val name: String, val avatarResId: Int)

Java:

public class User {
    private String name;
    private int avatarResId;

    // 构造函数、Getter和Setter...
}

步骤 5:创建 ViewHolder

创建一个继承自 RecyclerView.ViewHolder 的类,用于持有列表项视图。

Kotlin:

class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val tvName: TextView = itemView.findViewById(R.id.tvName)
    private val ivAvatar: ImageView = itemView.findViewById(R.id.ivAvatar)

    fun bind(user: User) {
        tvName.text = user.name
        ivAvatar.setImageResource(user.avatarResId)
    }
}

Java:

public class UserViewHolder extends RecyclerView.ViewHolder {
    TextView tvName;
    ImageView ivAvatar;

    public UserViewHolder(@NonNull View itemView) {
        super(itemView);
        tvName = itemView.findViewById(R.id.tvName);
        ivAvatar = itemView.findViewById(R.id.ivAvatar);
    }

    public void bind(User user) {
        tvName.setText(user.getName());
        ivAvatar.setImageResource(user.getAvatarResId());
    }
}

步骤 6:创建 Adapter

创建继承自 RecyclerView.Adapter 的适配器,这是核心部分。

Kotlin:

class UserAdapter(private val userList: List<User>) : RecyclerView.Adapter<UserViewHolder>() {

    // 创建新 ViewHolder(当没有可复用的 ViewHolder 时调用)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false)
        return UserViewHolder(itemView)
    }

    // 将数据绑定到 ViewHolder 上
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        val currentUser = userList[position]
        holder.bind(currentUser)
    }

    // 返回数据源的总数
    override fun getItemCount(): Int {
        return userList.size
    }
}

Java:

public class UserAdapter extends RecyclerView.Adapter<UserViewHolder> {
    private List<User> userList;

    public UserAdapter(List<User> userList) {
        this.userList = userList;
    }

    @NonNull
    @Override
    public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false);
        return new UserViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
        User currentUser = userList.get(position);
        holder.bind(currentUser);
    }

    @Override
    public int getItemCount() {
        return userList.size();
    }
}

步骤 7:在 Activity/Fragment 中组装

MainActivity 中,初始化所有部件并将它们连接起来。

Kotlin:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView: RecyclerView = findViewById(R.id.recyclerView)

        // 1. 准备数据
        val userList = listOf(
            User("Alice", R.drawable.avatar1),
            User("Bob", R.drawable.avatar2),
            User("Charlie", R.drawable.avatar3)
        )

        // 2. 创建适配器,并传入数据
        val adapter = UserAdapter(userList)

        // 3. 将适配器设置给 RecyclerView
        recyclerView.adapter = adapter

        // 4. 设置布局管理器(必须设置!)
        recyclerView.layoutManager = LinearLayoutManager(this)
        
        // 可选:添加默认的动画效果
        recyclerView.itemAnimator = DefaultItemAnimator()
    }
}

Java:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RecyclerView recyclerView = findViewById(R.id.recyclerView);

        // 1. 准备数据
        List<User> userList = new ArrayList<>();
        userList.add(new User("Alice", R.drawable.avatar1));
        userList.add(new User("Bob", R.drawable.avatar2));
        userList.add(new User("Charlie", R.drawable.avatar3));

        // 2. 创建适配器,并传入数据
        UserAdapter adapter = new UserAdapter(userList);

        // 3. 将适配器设置给 RecyclerView
        recyclerView.setAdapter(adapter);

        // 4. 设置布局管理器(必须设置!)
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        
        // 可选:添加默认的动画效果
        recyclerView.setItemAnimator(new DefaultItemAnimator());
    }
}

二、常用功能

1. 不同的布局管理器

  • 线性布局(垂直/水平)

    recyclerView.layoutManager = LinearLayoutManager(context)
    // 水平滚动
    // recyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
    
  • 网格布局

    // 2列的网格
    recyclerView.layoutManager = GridLayoutManager(context, 2)
    
  • 瀑布流布局

    // 2列的瀑布流,需要每个item高度不一致
    recyclerView.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
    

2. 添加点击事件

AdapteronBindViewHolder 中为 itemView 设置点击监听。

在 Adapter 中:

override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
    val currentUser = userList[position]
    holder.bind(currentUser)

    // 设置点击事件
    holder.itemView.setOnClickListener {
        // 通过接口回调给Activity处理
        listener.onItemClick(currentUser)
    }
}

// 定义回调接口
interface OnItemClickListener {
    fun onItemClick(user: User)
}

在 Activity 中实现接口:

class MainActivity : AppCompatActivity(), UserAdapter.OnItemClickListener {
    // ... 其他代码 ...
    override fun onItemClick(user: User) {
        // 处理点击事件,例如跳转到详情页或显示Toast
        Toast.makeText(this, "Clicked: ${user.name}", Toast.LENGTH_SHORT).show()
    }
}
// 创建Adapter时,将this(即Activity)作为listener传入
val adapter = UserAdapter(userList, this)

3. 动态更新数据

不要直接修改原始列表并调用 notifyDataSetChanged(),这样效率低且没有动画。

推荐做法:

// 在Adapter中维护一个可变的列表
private var userList = mutableListOf<User>()
    set(value) {
        field = value
        notifyDataSetChanged() // 全量更新,简单但效率低
    }

// 更高效的方法:使用DiffUtil(见下节)
fun updateData(newList: List<User>) {
    val diffResult = DiffUtil.calculateDiff(UserDiffCallback(this.userList, newList))
    this.userList.clear()
    this.userList.addAll(newList)
    diffResult.dispatchUpdatesTo(this)
}

4. 高性能数据更新 - DiffUtil

DiffUtil 可以计算新旧数据集的差异,并只更新变化的项,伴有动画。

class UserDiffCallback(
    private val oldList: List<User>,
    private val newList: List<User>
) : DiffUtil.Callback() {

    override fun getOldListSize(): Int = oldList.size
    override fun getNewListSize(): Int = newList.size

    // 判断两个item是否是同一个对象(例如id相同)
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].id == newList[newItemPosition].id
    }

    // 判断两个item的内容是否相同
    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition] == newList[newItemPosition]
    }
}

5. 添加头部和底部

可以通过在 Adapter 中创建不同的 viewType 来实现。

  1. 修改 getItemViewType 根据位置返回不同的类型。
  2. onCreateViewHolder 中根据 viewType 创建不同的 ViewHolder
  3. getItemCount 中返回 数据源大小 + 2(头尾)。
  4. onBindViewHolder 中根据位置和类型绑定数据。

6. 下拉刷新

使用 SwipeRefreshLayout 包裹 RecyclerView

布局:

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@+id/swipeRefreshLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

代码:

swipeRefreshLayout.setOnRefreshListener {
    // 执行刷新数据的操作
    fetchNewData()
    // 数据获取完成后,停止刷新动画
    swipeRefreshLayout.isRefreshing = false
}

总结

核心组件 作用 关键方法/类
RecyclerView 列表容器 在布局中定义
Adapter 数据与视图的桥梁 onCreateViewHolder, onBindViewHolder, getItemCount
ViewHolder 缓存列表项视图 继承自 RecyclerView.ViewHolder
LayoutManager 控制布局排列 LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager
ItemDecoration 绘制分割线 DividerItemDecoration
ItemAnimator 项动画 DefaultItemAnimator
DiffUtil 高效更新数据 DiffUtil.calculateDiff()

掌握这些基本用法和常用功能,你就能应对绝大多数列表展示的需求了。RecyclerView 的强大之处在于其高度的可定制性,你可以通过自定义 ItemDecorationItemAnimatorLayoutManager 来实现非常复杂和炫酷的列表效果。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容