Android 开发日记-RecycleView

1,430 阅读3分钟

简单列表实现

在app开发中列表肯定是最常见的样式,这里记录一下Android是如何使用RecyclerView创建动态列表的

类比一下 RecyclerView 相当于 iOS 中的 UITableView和UICollectionView

RecyclerView

RecyclerView 可以让您轻松高效地显示大量数据。您提供数据并定义每个列表项的外观,而 RecyclerView 库会根据需要动态创建元素。

顾名思义,RecyclerView 会回收这些单个的元素。当列表项滚动出屏幕时,RecyclerView 不会销毁其视图。相反,RecyclerView 会对屏幕上滚动的新列表项重用该视图。这种重用可以显著提高性能,改善应用响应能力并降低功耗。

RecyclerView 总的来说是列表元素的容器,然后复用View节省内容,提高性能。

RecyclerView 想要显示内容需要设置两个关键属性 layoutManageradapter

// 获取 recyclerView
recyclerView = findViewById(R.id.content_recycleview)
// 设置布局方式
recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, true)
// 绑定 adapter
recyclerView.adapter = textAdapeter

LayoutManager

  • LinearLayoutManager 线性布局,支持横向和纵向滑动,还可以翻转布局,翻转真的是适合做聊天室,因为翻转后布局的最后一条显示在屏幕的最下方。这一点iOS望尘莫及啊!
  • GridLayoutManager 二维网格排列,这个和 iOS 的 UICollectionView 的排列就很像了,没啥可说的。
  • StaggeredGridLayoutManager 和上面类似,但是同一列元素的高度可以不一样,不知道这个可不可以做瀑布流。

RecycleView示例图.png

Mac Android Studio 连接 mumu 模拟器

RecyclerView.Adapter 和 RecyclerView.ViewHolder

Adapter 用 iOS 的话说就是 UITableView 的 dataSource 了

ViewHolder 持有 itemView, 也就是相当于给 Cell 包装了一层

public abstract static class Adapter<VH extends ViewHolder> {
    ...
}

public abstract static class ViewHolder {
    @NonNull
    public final View itemView;
    ...
    public ViewHolder(@NonNull View itemView) {
        if (itemView == null) {
            throw new IllegalArgumentException("itemView may not be null");
        }
        this.itemView = itemView;
    }
    ...
}
  • 一个 RecyclerView 同时只对应一个 Adapter
  • 自定义的 Adapter 继承的时候需要指定一个 ViewHolder
  • ViewHolder 初始化需要传入 itemView

在看一下继承 Adapter 需要实现的方法

import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

// 定义MyAdapter
class MyAdapter: RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    // 定义 ViewHolder
    class MyViewHolder(view: View): RecyclerView.ViewHolder(view) {

    }

    // 需要在这里对ViewHolder实例化
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        // 先实例化一个 itemView
        val itemView = LayoutInflater.from(parent.context).inflate(R.layout.cell_text, parent, false)
        // 在实例化 ViewHolder
        return MyViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        // 这里就是可以根据 holder 获取 itemView
        holder.itemView
        // 然后就可以嘿嘿嘿,不用说了吧
    }

    override fun getItemCount(): Int {
        // 返回显示的个数,这个不用说了吧
    }
}

然后给 recyclerView 设置下布局,绑定下adapter OK了。

// 设置布局方式
recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, true)

// 绑定 adapter
recyclerView.adapter = MyAdapeter()

实现多种样式

实际应用中,列表中显示的内容不可能永远都是一种样式,那么多样式是怎么实现的那这里记录一下

方法1

根据 viewType 在 onCreateViewHolder 中返回不同的 View, 复用规则是根据 viewType 的值来的(盲猜一下应该是这样)

abstract class MyViewHolder(view: View): RecyclerView.ViewHolder(view) {
    ...
}

class MyTextViewHolder(view: View): MyViewHolder(view) {
    ...
}

class MyImageViewHolder(view: View): MyViewHolder(view) {
    ...
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    val itemView = ...
    val itemView2 = ...
    // plan 1
    if (viewType == 1) {
        return MyTextViewHolder(itemView)
    } else {
        return MyImageViewHolder(itemView2)
    }
    // plan 2
    if (viewType == 1) {
        return MyTextViewHolder(itemView)
    } else {
        return MyTextViewHolder(itemView2)
    }
    
}
override fun getItemViewType(position: Int): Int {
    if (position < 10) 
        return 1
    return 2
}

方法2 ConcatAdapter

implementation 'androidx.recyclerview:recyclerview:1.2.1'

ConcatAdapter 像iOS里面的 Section 概念

ConcatAdapter 可以将多个不同的 Adapter 当作一个个单独的 Section 处理

// 初始化adapter
val textAdapeter = MyAdapter()
val imageAdapter = MyAdapter2()

// 绑定 adapter
val dAdapter = ConcatAdapter(textAdapeter, imageAdapter)
recyclerView.adapter = dAdapter

screenshot_20220417_162949.png