23、空视图处理:列表为空时显示“暂无数据”提示。

做列表开发,有个场景特别常见——数据还没加载完,或者压根就没有数据。这时候你给用户看一个空白页面,说实话,挺不友好的。用户会想:「这 App 是不是卡死了?」或者「我操作错了?」。

我个人习惯,只要列表可能为空,就一定给它配一个空视图。说白了,就是告诉用户:「嘿,现在没东西,但我知道你没做错什么。」

为什么需要空视图?

你想想看,一个 RecyclerView 绑定了空的数据集,它渲染出来就是一块白板。用户盯着白板,心里没底。我遇到过不止一次,测试同学提 bug 说「页面加载不出来」,结果一看,是数据为空,但没有任何提示。

空视图的作用其实就三点:

  • 消除用户焦虑——明确告知当前状态
  • 引导下一步操作——比如「点击刷新」或「去添加数据」
  • 提升专业感——空白页面显得很粗糙,有提示才完整

核心原则:永远不要让用户面对一个空白的 RecyclerView。要么显示数据,要么显示提示。

两种实现思路

实现空视图,我一般用两种方式。哪种顺手就用哪种,没有绝对的对错。

方式一:直接在布局中控制可见性

这是最直接的做法。在 Activity 或 Fragment 的布局里,把 RecyclerView 和一个空视图布局放在一起。然后根据数据量,切换它们的可见性。

举个例子:

<!-- fragment_list.xml -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    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" />

    <LinearLayout
        android:id="@+id/emptyView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:visibility="gone">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="暂无数据"
            android:textSize="16sp"
            android:textColor="#999999" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="刷新"
            android:onClick="onRefreshClick" />

    </LinearLayout>

</FrameLayout>

然后在代码里:

fun updateList(data: List<Item>) {
    if (data.isEmpty()) {
        recyclerView.visibility = View.GONE
        emptyView.visibility = View.VISIBLE
    } else {
        recyclerView.visibility = View.VISIBLE
        emptyView.visibility = View.GONE
        adapter.submitList(data)
    }
}

嗯,这里要注意:submitList 调用时如果传空列表,RecyclerView 不会崩溃,但会显示空白。所以判断空数据的时机,最好在调用 submitList 之前。

方式二:使用 Adapter 的观察者模式

这种方式更优雅一些。利用 AdapterDataObserver 监听数据变化,自动切换空视图。我比较推荐这种方式,因为它把逻辑封装在了 Adapter 或 ViewModel 层,UI 层只需要设置一次监听。

看代码:

class MyAdapter : ListAdapter<Item, MyAdapter.ViewHolder>(DiffCallback()) {

    private var emptyView: View? = null

    fun attachEmptyView(view: View) {
        emptyView = view
        registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
            override fun onChanged() {
                checkEmpty()
            }

            override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
                checkEmpty()
            }

            override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
                checkEmpty()
            }
        })
    }

    private fun checkEmpty() {
        emptyView?.visibility = if (itemCount == 0) View.VISIBLE else View.GONE
    }
}

使用的时候:

adapter.attachEmptyView(findViewById(R.id.emptyView))

这样做的好处是,你不需要在每次更新数据时都手动判断可见性。Adapter 自己会搞定。我在项目中用过这种方式,代码干净很多。

小技巧:如果你用 ListAdapter,记得在 onItemRangeInsertedonItemRangeRemoved 里也触发检查。因为 submitList 可能触发的是增量更新,不是全量 onChanged

空视图应该长什么样?

不只是「暂无数据」四个字。我建议至少包含:

  • 图标——一个简单的空盒子或文件夹图标,视觉上更友好
  • 主文案——「暂无数据」或「这里空空如也」
  • 副文案——解释原因或给出建议,比如「请检查网络后重试」
  • 操作按钮——「刷新」或「去添加」,让用户有路可走

举个例子:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="暂无数据"
    android:textSize="18sp"
    android:textStyle="bold" />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="当前没有内容,下拉刷新试试"
    android:textSize="14sp"
    android:layout_marginTop="8dp" />

避坑指南

我曾经踩过一个坑:onCreate 里直接判断数据源是否为空,然后设置空视图可见性。结果数据是异步加载的,初始时确实是空,但加载完成后数据来了,空视图却没隐藏。因为我没有在数据回调里更新可见性。

所以记住:空视图的切换逻辑,一定要和数据更新的回调绑定在一起。不要在初始化时一次性判断。

另外,如果你用 LiveDataFlow 观察数据,空视图的切换可以放在观察者的回调里。这样最安全。

SVG 流程图:空视图处理逻辑

下面这张图展示了从数据加载到空视图展示的完整流程:

加载数据 数据列表是否为空? 隐藏 RecyclerView 显示空视图 显示 RecyclerView 隐藏空视图 用户看到正确界面

这个流程其实很简单:数据来了,先判断是否为空。空就隐藏列表显示提示,非空就显示列表隐藏提示。关键是「判断」这个节点,一定要放在数据更新的回调里。

总结一下

空视图处理,说白了就是给用户一个交代。别让用户对着白板发呆。我个人建议优先使用 Adapter 观察者的方式,代码更解耦。如果你项目简单,直接控制可见性也没问题。

最后提醒一句:空视图的文案要友好,别写「数据为空」这种冷冰冰的提示。加个图标,加个操作按钮,用户体验会好很多。

额外建议:如果你做的是下拉刷新列表,空视图里可以提示「下拉刷新试试」。这样用户就知道下一步该做什么了。


公众号:蓝海资料掘金营,微信deep3321