Android 必知必会 - EmptyView

1,314 阅读2分钟

如果移动端访问不佳,请访问 ==> Github 版

关键词:GridView ListView EmptyView SwipeRefreshLayout

在使用 GridView、ListView 时经常需要处理无数据的情况,给用户一些必要的提示。而 GridView 和 ListView 可以使用 setEmptyView() 方法来设置无数据时展示的 View 。

本文讲一下 EmptyView 的基本用法以及如何配合 SwipeRefreshLayout 使用。

背景知识点

FrameLayout 布局方式的特点

FrameLayout 是对其子 View 约束最少最简单的布局,所有放在 FrameLayout 里的控件,都按照层次堆叠在屏幕的左上角。后加进来的控件覆盖前面的控件。

嗯,就像喷漆,后面喷的总是覆盖在之前喷的上面。

setEmptyView() 都做了什么?

setEmptyView(View emptyView)AdapterView 的一个方法,ListView、GridView、Spinner 和 Gallery 都是 AdapterView 的子类,那么理论上来说,本文讲解的针对 ListView、GridView 设置 EmptyView 的方法对于 Spinner、Gallery 应该同样适用。

下面简单看下这个方法的源码:

public void setEmptyView(View emptyView) {
        mEmptyView = emptyView;

        // If not explicitly specified this view is important for accessibility.
        if (emptyView != null
                && emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
        }

        final T adapter = getAdapter();
        final boolean empty = ((adapter == null) || adapter.isEmpty());
        updateEmptyStatus(empty);
    }

/**
     * Update the status of the list based on the empty parameter.  If empty is true and
     * we have an empty view, display it.  In all the other cases, make sure that the listview
     * is VISIBLE and that the empty view is GONE (if it's not null).
     */
    private void updateEmptyStatus(boolean empty) {
        if (isInFilterMode()) {
            empty = false;
        }

        if (empty) {
            if (mEmptyView != null) {
                mEmptyView.setVisibility(View.VISIBLE);
                setVisibility(View.GONE);
            } else {
                // If the caller just removed our empty view, make sure the list view is visible
                setVisibility(View.VISIBLE);
            }

            // We are now GONE, so pending layouts will not be dispatched.
            // Force one here to make sure that the state of the list matches
            // the state of the adapter.
            if (mDataChanged) {           
                this.onLayout(false, mLeft, mTop, mRight, mBottom); 
            }
        } else {
            if (mEmptyView != null) mEmptyView.setVisibility(View.GONE);
            setVisibility(View.VISIBLE);
        }
    }

基本就是:

  • 数据为空
    • setVisibility(View.GONE); 隐藏自身
    • mEmptyView.setVisibility(View.VISIBLE); 显示 EmptyView
  • 数据不为空
    • mEmptyView.setVisibility(View.GONE); 隐藏 EmptyView
    • setVisibility(View.VISIBLE); 显示自身

当然,中间还有一些非空判断等。

用法

这里需要说明一点,EmptyView 只要是一个 View 即可,所以除了基本控件(TextView|Button 等)外,也可以是嵌套的布局(LinearLayout|RelativeLayout 等)。

基础用法

布局:

  • LinearLayout|FrameLayout|RelativeLayout
    • GridView|ListView (id = dataView)
    • View( id = mEmptyView ,任意 View,可嵌套)

关键代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.xxx);
    mDataView = (GridView|ListView) findViewById(R.id.dataView);
    adapter = xxxx;
    mDataView.setAdapter(adapter);
    mDataView.setEmptyView(findViewById(R.id.mEmptyView));
}

PS:务必在 setAdapter() 之后调用 setEmptyView()

配合 SwipeRefreshLayout 使用

配合 SwipeRefreshLayout 使用和基础用法类似,唯一需要注意的是布局的嵌套关系。

布局:

  • FrameLayout
    • SwipeRefreshLayout
    • GridView|ListView (id = dataView)
    • View( id = mEmptyView ,任意 View,可嵌套)

关键代码没有变化。这里 GridView|ListViewSwipeRefreshLayout 内部,SwipeRefreshLayoutEmptyView 需要同级,且最好在 FrameLayout 内部。

总结

本文简单总结了两种最基本的用法,其他更高级更复杂嵌套的情况都可以参考这两种基本用法。

有什么建议或者问题可以随时联系我,共同探讨学习: