如何使用 RecyclerView 显示空视图?

How to show an empty view with a RecyclerView?

提问人:JJD 提问时间:1/29/2015 最后编辑:naXa stands with UkraineJJD 更新时间:9/4/2021 访问量:229735

问:

我习惯于在布局文件中放置一个特殊视图,如 ListActivity 文档中所述,以便在没有数据时显示。此视图的 id 为 。"android:id/empty"

<TextView
    android:id="@android:id/empty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/no_data" />

我想知道如何使用新的 RecyclerView 做到这一点?

Android 列表视图 android-recycler查看 无数据

评论

2赞 Hieu Rocker 8/12/2015
您可以使用 github.com/rockerhieu/rv-adapter-states,它不仅支持空视图,还支持加载视图和错误视图。您可以在不更改现有适配器的逻辑的情况下使用它。
36赞 Subby 5/11/2016
令人着迷的是,似乎没有一种简单的方法来对抗......setEmptyView()RecyclerView
0赞 Rakesh Verma 10/16/2019
这是我的答案,请查看此链接 stackoverflow.com/a/58411351/5449220
0赞 CoolMind 11/15/2019
@Subby,同意,但也有劣势。加载数据时,我们不想在 中显示空视图,但它确实如此。所以,我们必须实现一些逻辑。目前我使用 stackoverflow.com/a/48049142/2914140setEmptyView()ListView

答:

10赞 Pedro Oliveira 1/29/2015 #1

在适配器上,检查适配器是否有 0 个元素,如果有,则返回不同的 viewType。getItemViewType

然后检查 viewType 是否是您之前返回的那个,并夸大不同的视图。在本例中,具有该 TextView 的布局文件onCreateViewHolder

编辑

如果这仍然不起作用,那么您可能希望以编程方式设置视图的大小,如下所示:

Point size = new Point();
((WindowManager)itemView.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getSize(size);

然后,当您夸大视图调用时:

inflatedView.getLayoutParams().height = size.y;
inflatedView.getLayoutParams().width = size.x;

评论

0赞 JJD 2/6/2015
听上去很好。但是,当我的数据集为空时,我不会进入,因为我必须返回.onCreateViewHoldernull0getItemCount
0赞 Pedro Oliveira 2/6/2015
如果 null 则返回 1 :)
0赞 JJD 2/6/2015
有点工作。谢谢。- 次要细节是,我无法在回收器视图中将空列表项垂直居中。尽管如此,文本视图仍保留在屏幕顶部,并且用于父回收器视图。android:layout_gravity="center"android:layout_height="match_parent"
0赞 Pedro Oliveira 2/6/2015
这取决于您正在使用的行的布局。它应该match_parent它的高度。如果仍无法使其正常工作,请添加全局布局侦听器,并将其布局参数设置为与屏幕具有相同的高度和宽度。
0赞 JJD 2/6/2015
所有容器(活动、片段、线性布局、回收器视图、文本视图)都设置为 。尽管该行不会扩展到屏幕高度。android:layout_height="match_parent"
346赞 slellis 2/6/2015 #2

在定义 的相同布局上,添加 :RecyclerViewTextView

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical" />

<TextView
    android:id="@+id/empty_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:visibility="gone"
    android:text="@string/no_data_available" />

在 或 适当的回调中,检查馈送您的数据集是否为空。 如果数据集为空,则 也是空的。在这种情况下,该消息将显示在屏幕上。 如果没有,请更改其可见性:onCreateRecyclerViewRecyclerView

private RecyclerView recyclerView;
private TextView emptyView;

// ...

recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
emptyView = (TextView) rootView.findViewById(R.id.empty_view);

// ...

if (dataset.isEmpty()) {
    recyclerView.setVisibility(View.GONE);
    emptyView.setVisibility(View.VISIBLE);
}
else {
    recyclerView.setVisibility(View.VISIBLE);
    emptyView.setVisibility(View.GONE);
}

评论

15赞 JJD 2/6/2015
这对我不起作用,因为回收商视图布局在片段中被夸大了,而是显示正常项目布局还是非项目布局是在适配器中做出的。
2赞 Ray Hunter 4/13/2015
@JJD使用 Fragment 时,同样的事情也适用。当您扩充片段视图时,您有 recyclerview,然后是另一个将用于空视图的布局。通常在我的片段中,我会有以下视图:1)recyclerview,2)空视图,3)进度条视图。无论列表是什么,我通常都会传递适配器,但根据列表的大小,我要么隐藏 recyclerview 并显示空,反之亦然。如果 recyclerview 有一个空列表,则它不会对它执行任何操作。你只看到一个空的视图。
1赞 slellis 4/29/2015
@stackex 验证必须在 Fragment 或 Activity 的 onResume 的 onCreateView 回调中。这样,验证将在屏幕显示给用户之前立即进行,并且适用于两者,增加或减少数据集中的行数。
2赞 AgentKnopf 12/3/2015
@Zapnologica 您可以反其道而行之:使用 Eventbus(例如 greenrobots Eventbus 或 Otto),然后在数据集更改时将适配器放在 eventbus 上。在片段中,您所要做的就是创建一个方法,其参数对应于 Adapter 传递到 Eventbus.post 方法中的内容,然后相应地更改布局。一种更简单但也更耦合的方法是在适配器中创建一个回调接口,并在创建适配器时让片段实现它。
2赞 powder366 2/7/2016
在布局中,我得到了多个根标签。您的完整布局文件是什么样的?
203赞 maff91 5/24/2015 #3

对于我的项目,我制作了这个解决方案(使用方法):RecyclerViewsetEmptyView

public class RecyclerViewEmptySupport extends RecyclerView {
    private View emptyView;

    private AdapterDataObserver emptyObserver = new AdapterDataObserver() {


        @Override
        public void onChanged() {
            Adapter<?> adapter =  getAdapter();
            if(adapter != null && emptyView != null) {
                if(adapter.getItemCount() == 0) {
                    emptyView.setVisibility(View.VISIBLE);
                    RecyclerViewEmptySupport.this.setVisibility(View.GONE);
                }
                else {
                    emptyView.setVisibility(View.GONE);
                    RecyclerViewEmptySupport.this.setVisibility(View.VISIBLE);
                }
            }

        }
    };

    public RecyclerViewEmptySupport(Context context) {
        super(context);
    }

    public RecyclerViewEmptySupport(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RecyclerViewEmptySupport(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);

        if(adapter != null) {
            adapter.registerAdapterDataObserver(emptyObserver);
        }

        emptyObserver.onChanged();
    }

    public void setEmptyView(View emptyView) {
        this.emptyView = emptyView;
    }
}

你应该用它来代替类:RecyclerView

<com.maff.utils.RecyclerViewEmptySupport android:id="@+id/list1"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    />

<TextView android:id="@+id/list_empty"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Empty"
    />

RecyclerViewEmptySupport list = 
    (RecyclerViewEmptySupport)rootView.findViewById(R.id.list1);
list.setLayoutManager(new LinearLayoutManager(context));
list.setEmptyView(rootView.findViewById(R.id.list_empty));

评论

3赞 Sufian 7/27/2015
你是从这个答案还是从这里获取此代码?
0赞 maff91 7/30/2015
@Sufian 不,我没有看到那个答案,只是找到了同样的方式。但那个代码看起来肯定比我的漂亮:)
2赞 FrankkieNL 6/12/2016
我试图将其与FirebaseRecyclerAdapter结合使用,但没有用。这是因为 FirebaseRecyclerAdapter 不会调用(AdapterDataObserver 的)onChange,而是调用 onItem* 方法。要使其正常工作,请添加: ' Override public void onItemRangeRemoved(int positionStart, int itemCount) { //检查 } Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { //检查 } //等。`
1赞 Stimsoni 7/27/2016
为什么要检查适配器在观察器中是否为 null?如果适配器为 null,则永远不应调用观察者。
27赞 nww04 4/5/2017
呃,我必须说这只是经典的 Google BS。我必须实现它只是为了添加一个空视图?它应该包含在他们的 cr@psh1t SDK 中!看在上帝的份上!
57赞 wnc_21 10/15/2015 #4

我使用ViewSwitcher

<ViewSwitcher
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/switcher"
    >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <TextView android:id="@+id/text_empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/list_empty"
        android:gravity="center"
        />

</ViewSwitcher>

在代码中,您将检查游标/数据集并切换视图。

void showItems(Cursor items) {
    if (items.size() > 0) {

        mAdapter.switchCursor(items);

        if (R.id.list == mListSwitcher.getNextView().getId()) {
            mListSwitcher.showNext();
        }
    } else if (R.id.text_empty == mListSwitcher.getNextView().getId()) {
        mListSwitcher.showNext();
    }
}

此外,如果您愿意,您可以使用几行代码设置动画

mListSwitcher.setInAnimation(slide_in_left);
mListSwitcher.setOutAnimation(slide_out_right);

评论

0赞 Zhang Xiang 11/24/2016
真的很新奇
0赞 Ojonugwa Jude Ochalifu 9/12/2018
干净的解决方案。
2赞 YTerle 1/29/2016 #5

我添加并替代了:RecyclerViewImageViewRelativeLayout

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/no_active_jobs"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@mipmap/ic_active_jobs" />

    <android.support.v7.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" />

</RelativeLayout>

然后在 :Adapter

@Override
public int getItemCount() {
    if (mOrders.size() == 0) {
        mRecyclerView.setVisibility(View.INVISIBLE);
    } else {
        mRecyclerView.setVisibility(View.VISIBLE);
    }
    return mOrders.size();
}

评论

7赞 Basheer AL-MOMANI 5/6/2016
如何到达mRecyclerViewAdapter
2赞 ruX 7/27/2016
将适配器绑定到特定 Activity 不是很好的设计模式。最好通过接口来做到这一点
1赞 Felipe Conde 9/13/2016
此外,这会导致布局过度绘制,不建议这样做,并且可能会导致一些性能问题。有关详细信息,请参阅 youtube.com/watch?v=T52v50r-JfE
73赞 radu122 3/7/2016 #6

下面是一个解决方案,它仅使用具有不同视图类型的自定义适配器来应对空情况。

public class EventAdapter extends 
    RecyclerView.Adapter<EventAdapter.ViewHolder> {

    private static final int VIEW_TYPE_EVENT = 0;
    private static final int VIEW_TYPE_DATE = 1;
    private static final int VIEW_TYPE_EMPTY = 2;

    private ArrayList items;

    public EventAdapter(ArrayList items) {
        this.items = items;
    }

    @Override
    public int getItemCount() {
        if(items.size() == 0){
            return 1;
        }else {
            return items.size();
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (items.size() == 0) {
            return VIEW_TYPE_EMPTY;
        }else{
            Object item = items.get(position);
            if (item instanceof Event) {
                return VIEW_TYPE_EVENT;
            } else {
                return VIEW_TYPE_DATE;
            }
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        ViewHolder vh;
        if (viewType == VIEW_TYPE_EVENT) {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event, parent, false);
            vh = new ViewHolderEvent(v);
        } else if (viewType == VIEW_TYPE_DATE) {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event_date, parent, false);
            vh = new ViewHolderDate(v);
        } else {
            v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.item_event_empty, parent, false);
            vh = new ViewHolder(v);
        }

        return vh;
    }

    @Override
    public void onBindViewHolder(EventAdapter.ViewHolder viewHolder, 
                                 final int position) {
        int viewType = getItemViewType(position);
        if (viewType == VIEW_TYPE_EVENT) {
            //...
        } else if (viewType == VIEW_TYPE_DATE) {
            //...
        } else if (viewType == VIEW_TYPE_EMPTY) {
            //...
        }
    }

    public static class ViewHolder extends ParentViewHolder {
        public ViewHolder(View v) {
            super(v);
        }
    }

    public static class ViewHolderDate extends ViewHolder {
        public ViewHolderDate(View v) {
            super(v);
        }
    }

    public static class ViewHolderEvent extends ViewHolder {
        public ViewHolderEvent(View v) {
            super(v);
        }
    }

}

评论

3赞 MSpeed 4/15/2017
@sfk92fksdf 为什么容易出错?这是您应该在 recyclerview 中处理多个视图类型的方式
0赞 Alexey Andronov 4/16/2017
@billynomates,因为应该是一个单行本,但在这里我们有一个不平凡的,有一些隐藏的逻辑。这种逻辑也显得很尴尬,天知道还有什么地方getItemCount()ifgetItemViewType
5赞 MSpeed 4/17/2017
它不需要是一行。如果您有多种视图类型,则可以采用此方法。
1赞 Ivo 9/12/2019
在我看来,这个答案应该是公认的答案。这确实是处理多个视图类型的方法
1赞 Araz 5/4/2022
完善!谢谢!关键是空适配器不起作用!它不会调用它的 onBindViewHolder(),但将项目计数设置为 1 就可以了!
22赞 Sheharyar 5/13/2017 #7

RVEmptyObserver

与其使用自定义,不如扩展 an 是一种更简单的解决方案,它允许设置当列表中没有项目时显示的自定义:RecyclerViewAdapterDataObserverView

用法示例:

RVEmptyObserver observer = new RVEmptyObserver(recyclerView, emptyView)
rvAdapter.registerAdapterDataObserver(observer);

类:

public class RVEmptyObserver extends RecyclerView.AdapterDataObserver {
    private View emptyView;
    private RecyclerView recyclerView;

    public RVEmptyObserver(RecyclerView rv, View ev) {
        this.recyclerView = rv;
        this.emptyView    = ev;
        checkIfEmpty();
    }

    private void checkIfEmpty() {
        if (emptyView != null && recyclerView.getAdapter() != null) {
            boolean emptyViewVisible = recyclerView.getAdapter().getItemCount() == 0;
            emptyView.setVisibility(emptyViewVisible ? View.VISIBLE : View.GONE);
            recyclerView.setVisibility(emptyViewVisible ? View.GONE : View.VISIBLE);
        }
    }

    public void onChanged() { checkIfEmpty(); }
    public void onItemRangeInserted(int positionStart, int itemCount) { checkIfEmpty(); }
    public void onItemRangeRemoved(int positionStart, int itemCount) { checkIfEmpty(); }
}

评论

0赞 Geekarist 10/23/2017
我相信如果你不打电话,它会运行得更快checkIfEmpty()onChanged()onItemRangeInserted()
0赞 Sheharyar 10/24/2017
我同意(这是我个人所做的),但这只是一个面临这个问题的人的起点。
0赞 Nadim Ansari 11/19/2021
最好和完美的答案!
9赞 Linh 7/14/2017 #8

这是我的类,用于显示空视图、重试视图(当加载 api 失败时)和加载进度RecyclerView

public class RecyclerViewEmptyRetryGroup extends RelativeLayout {
    private RecyclerView mRecyclerView;
    private LinearLayout mEmptyView;
    private LinearLayout mRetryView;
    private ProgressBar mProgressBar;
    private OnRetryClick mOnRetryClick;

    public RecyclerViewEmptyRetryGroup(Context context) {
        this(context, null);
    }

    public RecyclerViewEmptyRetryGroup(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RecyclerViewEmptyRetryGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void onViewAdded(View child) {
        super.onViewAdded(child);
        if (child.getId() == R.id.recyclerView) {
            mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
            return;
        }
        if (child.getId() == R.id.layout_empty) {
            mEmptyView = (LinearLayout) findViewById(R.id.layout_empty);
            return;
        }
        if (child.getId() == R.id.layout_retry) {
            mRetryView = (LinearLayout) findViewById(R.id.layout_retry);
            mRetryView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mRetryView.setVisibility(View.GONE);
                    mOnRetryClick.onRetry();
                }
            });
            return;
        }
        if (child.getId() == R.id.progress_bar) {
            mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        }
    }

    public void loading() {
        mRetryView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.VISIBLE);
    }

    public void empty() {
        mEmptyView.setVisibility(View.VISIBLE);
        mRetryView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public void retry() {
        mRetryView.setVisibility(View.VISIBLE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public void success() {
        mRetryView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
    }

    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }

    public void setOnRetryClick(OnRetryClick onRetryClick) {
        mOnRetryClick = onRetryClick;
    }

    public interface OnRetryClick {
        void onRetry();
    }
}

activity_xml

<...RecyclerViewEmptyRetryGroup
        android:id="@+id/recyclerViewEmptyRetryGroup">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"/>

        <LinearLayout
            android:id="@+id/layout_empty">
            ...
        </LinearLayout>

        <LinearLayout
            android:id="@+id/layout_retry">
            ...
        </LinearLayout>

        <ProgressBar
            android:id="@+id/progress_bar"/>

</...RecyclerViewEmptyRetryGroup>

enter image description here

来源在这里 https://github.com/PhanVanLinh/AndroidRecyclerViewWithLoadingEmptyAndRetry

0赞 knightcube 7/21/2017 #9

以防万一您正在使用 FirebaseRecyclerAdapter,这篇文章可以作为魅力 https://stackoverflow.com/a/39058636/6507009

评论

8赞 Anh Pham 7/21/2017
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。
50赞 wonsuc 1/1/2018 #10

由于凯文的答案并不完整。
如果使用 's 和 更新数据集,这是更正确的答案。 请参阅下面其他用户添加的 Kotlin 版本。
RecyclerAdapternotifyItemInsertednotifyItemRemoved

爪哇岛:

mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {

    @Override
    public void onChanged() {
        super.onChanged();
        checkEmpty();
    }

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        super.onItemRangeInserted(positionStart, itemCount);
        checkEmpty();
    }

    @Override
    public void onItemRangeRemoved(int positionStart, int itemCount) {
        super.onItemRangeRemoved(positionStart, itemCount);
        checkEmpty();
    }

    void checkEmpty() {
        mEmptyView.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
    }
});

科特林

adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
    override fun onChanged() {
        super.onChanged()
        checkEmpty()
    }

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

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

    fun checkEmpty() {
        empty_view.visibility = (if (adapter.itemCount == 0) View.VISIBLE else View.GONE)
    }
})

评论

0赞 Glaucus 6/26/2019
这看起来不错,但如果您多次调用 swapAdapter() 甚至 setAdapter(),则可能不起作用。
0赞 Mr T 4/1/2020
干得好。显示标签时不要忘记隐藏 RecyclerView
9赞 Rasoul Miri 10/9/2018 #11

在自定义 RecyclerView 中使用 AdapterDataObserver

科特林:

RecyclerViewEnum.kt

enum class RecyclerViewEnum {
    LOADING,
    NORMAL,
    EMPTY_STATE
}

RecyclerViewEmptyLoadingSupport.kt

class RecyclerViewEmptyLoadingSupport : RecyclerView {

    var stateView: RecyclerViewEnum? = RecyclerViewEnum.LOADING
        set(value) {
            field = value
            setState()
        }
    var emptyStateView: View? = null
    var loadingStateView: View? = null


    constructor(context: Context) : super(context) {}

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {}


    private val dataObserver = object : AdapterDataObserver() {
        override fun onChanged() {
            onChangeState()
        }

        override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
            super.onItemRangeRemoved(positionStart, itemCount)
            onChangeState()
        }

        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            super.onItemRangeInserted(positionStart, itemCount)
            onChangeState()
        }
    }


    override fun setAdapter(adapter: RecyclerView.Adapter<*>?) {
        super.setAdapter(adapter)
        adapter?.registerAdapterDataObserver(dataObserver)
        dataObserver.onChanged()
    }


    fun onChangeState() {
        if (adapter?.itemCount == 0) {
            emptyStateView?.visibility = View.VISIBLE
            loadingStateView?.visibility = View.GONE
            [email protected] = View.GONE
        } else {
            emptyStateView?.visibility = View.GONE
            loadingStateView?.visibility = View.GONE
            [email protected] = View.VISIBLE
        }
    }

    private fun setState() {

        when (this.stateView) {
            RecyclerViewEnum.LOADING -> {
                loadingStateView?.visibility = View.VISIBLE
                [email protected] = View.GONE
                emptyStateView?.visibility = View.GONE
            }

            RecyclerViewEnum.NORMAL -> {
                loadingStateView?.visibility = View.GONE
                [email protected] = View.VISIBLE
                emptyStateView?.visibility = View.GONE
            }
            RecyclerViewEnum.EMPTY_STATE -> {
                loadingStateView?.visibility = View.GONE
                [email protected] = View.GONE
                emptyStateView?.visibility = View.VISIBLE
            }
        }
    }
}

布局:.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="vertical">

    <LinearLayout
        android:id="@+id/emptyView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/emptyLabelTv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="empty" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/loadingView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:gravity="center"
        android:orientation="vertical">

        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="45dp"
            android:layout_height="45dp"
            android:layout_gravity="center"
            android:indeterminate="true"
            android:theme="@style/progressBarBlue" />
    </LinearLayout>

    <com.peeyade.components.recyclerView.RecyclerViewEmptyLoadingSupport
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

在活动中,使用以下方式:

recyclerView?.apply {
        layoutManager = GridLayoutManager(context, 2)
        emptyStateView = emptyView
        loadingStateView = loadingView
        adapter = adapterGrid
    }

    // you can set LoadingView or emptyView manual
    recyclerView.stateView = RecyclerViewEnum.EMPTY_STATE
    recyclerView.stateView = RecyclerViewEnum.LOADING

评论

2赞 Andrii Artamonov 11/1/2019
如果您使用该解决方案,请注意,由于其内存分配大小增加,使用枚举是不够的。使用“整数”或“字符串”,具体取决于您的上下文,并带有适当的注释或注释StringDefIntDef
0赞 Sakiboy 3/23/2021
@AndriiArtamonov不,没关系。使用枚举 - stackoverflow.com/a/54893141/2371425
7赞 hidd 10/22/2019 #12

另一种方法是使用中出现/消失的子视图的句柄。addOnChildAttachStateChangeListenerRecyclerView

recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
            @Override
            public void onChildViewAttachedToWindow(@NonNull View view) {
                forEmptyTextView.setVisibility(View.INVISIBLE);
            }

            @Override
            public void onChildViewDetachedFromWindow(@NonNull View view) {
                forEmptyTextView.setVisibility(View.VISIBLE);
            }
        });

评论

0赞 Yusuph wickama 7/18/2022
这应该是正确的答案。要添加,在这两种方法中,您可以使用来决定。recyclerView.getAdapter().getItemCount()
0赞 Fitsum Alemu 11/13/2020 #13

如果要在回收器视图为空时显示文本视图,可以这样做:

ArrayList<SomeDataModel> arrayList = new ArrayList<>();

RecycleAdapter recycleAdapter =  new RecycleAdapter(getContext(),project_Ideas);

recyclerView..setAdapter(recycleAdapter);

if(arrayList.isEmpty())
{
    emptyTextView.setVisibility(View.VISIBLE);
    recyclerView.setVisibility(View.GONE);
}

我假设你有 TextView

和像这样的 XML

 android:visibility="gone"

    
  
1赞 iamnaran 5/3/2021 #14

最简单的解决方案是使用 RecyclerView.AdapterDataObserver,并在初始化适配器后将其注册到 recyclerview 中。

val emptyDataObserver = EmptyDataObserver(recycler_view, empty_data_parent)
yourAdapter.registerAdapterDataObserver(emptyDataObserver)

在您的活动中,recycler_view和empty_data_parent布局的位置,根据需要限制这些视图并使其可见性消失。然后创建你自己的空数据集视图,包括图像和文本。

 <include
            android:id="@+id/empty_data_parent"
            layout="@layout/item_empty_dataset"
            android:layout_width="match_parent"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="parent"
            android:layout_height="match_parent"
            android:layout_gravity="center" />

下面是一个empty_data_set_view.xml示例

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:orientation="vertical"
    android:layout_gravity="center"
    android:gravity="center"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    tools:ignore="RtlHardcoded">

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.422"
        android:src="@drawable/ic_empty_dataset_1" />

    <TextView
        android:id="@+id/title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Title is called when title is placed"
        android:padding="10dp"
        android:fontFamily="@font/normal"
        android:textStyle="bold"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView2" />

    <TextView
        android:id="@+id/sub_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text=" Subtitle is called when title is placed. Subtitle is called when title is placed"
        android:padding="5dp"
        android:fontFamily="@font/normal"
        android:gravity="center"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>

这个 EmptyDataObserver 类将完成这项工作。

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


class EmptyDataObserver constructor(rv: RecyclerView?, ev: View?): RecyclerView.AdapterDataObserver() {

    private var emptyView: View? = null
    private var recyclerView: RecyclerView? = null

    init {
        recyclerView = rv
        emptyView = ev
        checkIfEmpty()
    }


    private fun checkIfEmpty() {
        if (emptyView != null && recyclerView!!.adapter != null) {
            val emptyViewVisible = recyclerView!!.adapter!!.itemCount == 0
            emptyView!!.visibility = if (emptyViewVisible) View.VISIBLE else View.GONE
            recyclerView!!.visibility = if (emptyViewVisible) View.GONE else View.VISIBLE
        }
    }

    override fun onChanged() {
        super.onChanged()
        checkIfEmpty()
    }

    override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
        super.onItemRangeChanged(positionStart, itemCount)
    }

}
0赞 Ibrahim AbdelGawad 9/4/2021 #15

这如何在使用 LiveData 过滤和更新 RecyclerView 时显示空视图

    ViewModel.getInstance().getCustomers?.observe(viewLifecycleOwner, {customerList ->
           //assign your adapter with your list then
        listAdapter?.notifyDataSetChanged()
        isListItemEmpty(customerList?.isEmpty())
}

创建一个接口以在适配器和片段之间进行通信,并在适配器中注册它

 interface EmptyListener {
            fun isListEmpty(isEmpty: Boolean)
        }

然后在 publishResults 中调用它:

        dataList?.isEmpty()?.let { mListener?.isListEmpty(it) }

最后,在 Fragment 中实现您的接口:

 override fun isListEmpty(isEmpty: Boolean) {
        if (isEmpty) {
            your_list?.visibility = View.GONE
            empty_view?.visibility = View.VISIBLE
        } else {
            empty_view?.visibility = View.GONE
            your_list?.visibility = View.VISIBLE
        }
    }